﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;
using System.Windows.Forms;

using Noris.Schedule.Support.Services;
using Noris.Schedule.Support.Data;
using Noris.Schedule.Support.Core;
using System.Data;
using Noris.Schedule.Support.Sql;
using Noris.Schedule.Support.DataFace;
using System.Threading;

namespace Noris.Schedule.Support
{
	// CLASS Steward
	/// <summary>
	/// Hlavní správce podkladových dat aplikace.
	/// Používá se pro získání dat trvalého charakteru i pro volání systémových metod.
	/// </summary>
	/// <remarks>
	/// Obsahuje privátní singleton na svoji instanci, v níž uchovává trvalá data.
	/// Uchovává v sobě konfiguraci, reflexi výkonných tříd.
	/// </remarks>
	public partial class Steward
	{
		#region STATIC METODY - statický vstup, parametry, vyzvednutí singleton instance, převolání instanční metody
		#region Systémové metody
		/// <summary>
		/// Povinná metoda. kdo ji nezavolá, ten nepojede.
		/// V parametrech je třeba předat soupis typů služeb, které chceme, aby steward registroval a uměl obsloužit (najít a vracet).
		/// </summary>
		/// <param name="findServiceTypes">Soupis služeb, které budeme používat (které chceme v rámci aplikace detekovat)</param>
		public static void AppInit()
		{
            WinRegSetRootDefaultFolder(WINREG_ROOTPATH);

			Current._ServicesLoad();
			Current._FunctionMenuInit();
			Current._AuditClear();
			Current._AppReady = true;
            Current._ExitProcesed = false;
            
            Steward.AuditCurrentProcess();
		}
		/// <summary>
		/// Metoda ověří, zda dané přihlašovací jsou korektní a funkční.
		/// Metoda vrací true = vše je OK / false = nelze se přilogovat.
		/// </summary>
		/// <param name="login">Jméno uživatele</param>
		/// <param name="password">Heslo</param>
		/// <param name="dbProfile">DB profil</param>
		/// <param name="error">Out text problému</param>
		/// <param name="user">Out uživatel, který danému loginu vyhovuje</param>
		/// <returns></returns>
		public static bool TryLogin(string login, string password, ConnectProfile dbProfile, out string error, out NorisUserCls user)
		{
			return Current._TryLogin(login, password, dbProfile, out error, out user);
		}
		/// <summary>
		/// Metoda zajistí okno Login, pokud je zapotřebí.
		/// </summary>
		public static bool LoginWhenRequired(int functionNumber, bool dataReady)
		{
			return Current._LoginWhenRequired(functionNumber, dataReady);
		}
		/// <summary>
		/// Pokud aplikace nějak získá funkční profil k připojení a profil uživatele, může se touto metodou připojit.
		/// Zde se nezkoumá právo ani korektnost dat, data se pouze uloží a nastaví se Steward.DataReady = Steward.ConnectReady.
		/// </summary>
		/// <param name="dbProfile"></param>
		/// <param name="user"></param>
		public static void Login(ConnectProfile dbProfile, NorisUserCls user, bool dataReady)
		{
			Steward.AppConfig.CurrentProfile = dbProfile;
			Steward.CurrentUser = user;
			if (dataReady)
				Steward.DataReady = (dbProfile != null && user != null && Steward.ConnectReady);              // Od této chvíle jsou data Ready (pokud je Ready connect profil)
		}
		/// <summary>
		/// Povinná metoda. Je třeba ji zavolat před koncem aplikace.
		/// </summary>
		public static void AppDone()
		{
            Current._AppExit();
			Current._FunctionMenuDispose();
			Current._ServicesUnLoad();
            Current._TraceClose();
		}
        private void _AppExit()
        {
            if (this._ExitProcesed) return;

            if (this.AppExiting != null)
                this.AppExiting(this, EventArgs.Empty);

            this._ExitProcesed = true;
        }
        /// <summary>
        /// Přidá požadovanou akci do seznamu událostí volaných na konci života aplikace.
        /// Typicky se používá k ukládání konfigurací.
        /// </summary>
        /// <param name="action"></param>
        public static void CallMeOnAppExit(System.EventHandler action)
        {
            Current.AppExiting += action;
        }
        private event EventHandler AppExiting;
        private bool _ExitProcesed = false;
		#endregion
		#region Parametry běhu - PUBLIC STATIC
		/// <summary>
		/// Detekce parametrů aplikace. Parametry jsou uloženy v Steward.ParamsList, ke čtení slouží metoda Steward.ParamGet().
		/// Parametry:
		/// N=UserName
		/// P=Password
		/// D=DataProfile name
		/// C=ConfigFile
		/// Sd=ConnectString (čitelný)
		/// Sc=ConnectString (Base64)
		/// Systém detekuje všechny vstupní parametry, zadané ve formě Text=Hodnota
		/// </summary>
		/// <param name="arguments">Soupis předaných parametrů</param>
		public static void ParameterStore(string[] arguments)
		{
			Current._ParamStore(arguments);
		}
		/// <summary>
		/// Najde a vrátí parametr, podle jeho daného klíče.
		/// Klíč je text uvedený před textem parametru, například:
		/// Parametry spuštění jsou    /C=data.config /User=lcs
		/// Pak klíčem je text "/C" nebo "/User", vrácenou hodnotou pak "data.config" nebo "lcs".
		/// Pokud parametr nenajde, vrátí null.
		/// Pokud parametr existuje, a je prázdný, vrací String.Empty
		/// Pokud chceme otestovat existenci, zavoláme metodu ParamExists(), nebo ParamCount().
		/// Pokud chceme získat více parametrů. použijeme metodu ParamsGet().
		/// </summary>
		/// <param name="key">Klíč parametru = text před rovnítkem (při očekávaném zadání např. /User=lcs je klíčem text "/User")</param>
		/// <param name="ignoreCase">Ignorovat velikost písmen, tzn. "/User" najde parametry: "/user" i "/USER"</param>
		/// <returns></returns>
		public static string ParamGet(string key, bool ignoreCase)
		{
			return Current._ParamGet(new string[] { key }, ignoreCase);
		}
		/// <summary>
		/// Najde a vrátí parametr, podle jeho daného klíče.
		/// Klíč je text uvedený před textem parametru, například:
		/// Parametry spuštění jsou    /C=data.config /User=lcs
		/// Pak klíčem je text "/C" nebo "/User", vrácenou hodnotou pak "data.config" nebo "lcs".
		/// Pokud parametr nenajde, vrátí null.
		/// Pokud parametr existuje, a je prázdný, vrací String.Empty
		/// Pokud chceme otestovat existenci, zavoláme metodu ParamExists(), nebo ParamCount().
		/// Pokud chceme získat více parametrů. použijeme metodu ParamsGet().
		/// </summary>
		/// <param name="keyList">Hledané parametry, vrací se první z nalezených. Klíč parametru = text před rovnítkem (při očekávaném zadání např. /User=lcs je klíčem text "/User")</param>
		/// <param name="ignoreCase">Ignorovat velikost písmen, tzn. "/User" najde parametry: "/user" i "/USER"</param>
		/// <returns></returns>
		public static string ParamGet(string[] keyList, bool ignoreCase)
		{
			return Current._ParamGet(keyList, ignoreCase);
		}
		/// <summary>
		/// Najde a vrátí parametry, které vyhovují danému klíči (daným klíčům), vrací je včetně jejich klíče.
		/// Klíč je text uvedený před textem parametru, například:
		/// Parametry spuštění jsou    /C=data.config /User=lcs
		/// Pak klíčem je text "/C" nebo "/User", vrácenou hodnotou pak "data.config" nebo "lcs".
		/// Pokud parametr nenajde, vrátí null.
		/// Pokud parametr existuje, a je prázdný, vrací String.Empty
		/// Pokud chceme otestovat existenci, zavoláme metodu ParamExists(), nebo ParamCount().
		/// Pokud chceme získat více parametrů. použijeme metodu ParamsGet().
		/// </summary>
		/// <param name="key">Klíč parametru = text před rovnítkem (při očekávaném zadání např. /User=lcs je klíčem text "/User")</param>
		/// <param name="ignoreCase">Ignorovat velikost písmen, tzn. "/User" najde parametry: "/user" i "/USER"</param>
		/// <returns></returns>
		public static List<KeyValuePair<string, string>> ParamsGet(string key, bool ignoreCase)
		{
			return Current._ParamsGet(new string[] { key }, ignoreCase);
		}
		/// <summary>
		/// Najde a vrátí parametry, které vyhovují danému klíči (daným klíčům), vrací je včetně jejich klíče.
		/// Klíč je text uvedený před textem parametru, například:
		/// Parametry spuštění jsou    /C=data.config /User=lcs
		/// Pak klíčem je text "/C" nebo "/User", vrácenou hodnotou pak "data.config" nebo "lcs".
		/// Pokud parametr nenajde, vrátí null.
		/// Pokud parametr existuje, a je prázdný, vrací String.Empty
		/// Pokud chceme otestovat existenci, zavoláme metodu ParamExists(), nebo ParamCount().
		/// Pokud chceme získat více parametrů. použijeme metodu ParamsGet().
		/// </summary>
		/// <param name="keyList">Hledané parametry, vrací se první z nalezených. Klíč parametru = text před rovnítkem (při očekávaném zadání např. /User=lcs je klíčem text "/User")</param>
		/// <param name="ignoreCase">Ignorovat velikost písmen, tzn. "/User" najde parametry: "/user" i "/USER"</param>
		/// <returns></returns>
		public static List<KeyValuePair<string, string>> ParamsGet(string[] keyList, bool ignoreCase)
		{
			return Current._ParamsGet(keyList, ignoreCase);
		}
		/// <summary>
		/// Najde a vrátí počet parametrů, které odpovídají danému klíči / klíčům.
		/// Klíč je text uvedený před textem parametru, například:
		/// Parametry spuštění jsou    /C=data.config /User=lcs
		/// Pak klíčem je text "/C" nebo "/User", vrácenou hodnotou pak "data.config" nebo "lcs".
		/// Pokud parametr nenajde, vrátí 0.
		/// </summary>
		/// <param name="key">Klíč parametru = text před rovnítkem (při očekávaném zadání např. /User=lcs je klíčem text "/User")</param>
		/// <param name="ignoreCase">Ignorovat velikost písmen, tzn. "/User" najde parametry: "/user" i "/USER"</param>
		/// <returns></returns>
		public static int ParamCount(string key, bool ignoreCase)
		{
			return Current._ParamCount(new string[] { key }, ignoreCase);
		}
		/// <summary>
		/// Najde a vrátí počet parametrů, které odpovídají danému klíči / klíčům.
		/// Klíč je text uvedený před textem parametru, například:
		/// Parametry spuštění jsou    /C=data.config /User=lcs
		/// Pak klíčem je text "/C" nebo "/User", vrácenou hodnotou pak "data.config" nebo "lcs".
		/// Pokud parametr nenajde, vrátí 0.
		/// </summary>
		/// <param name="keyList">Hledané parametry, vrací se první z nalezených. Klíč parametru = text před rovnítkem (při očekávaném zadání např. /User=lcs je klíčem text "/User")</param>
		/// <param name="ignoreCase">Ignorovat velikost písmen, tzn. "/User" najde parametry: "/user" i "/USER"</param>
		/// <returns></returns>
		public static int ParamCount(string[] keyList, bool ignoreCase)
		{
			return Current._ParamCount(keyList, ignoreCase);
		}
		/// <summary>
		/// Parametry spuštění.
		/// Key je hodnota před rovnítkem, Value je hodnota za rovnítkem.
		/// Pokud argument neobsahuje rovnítko, pak je Key prázdné.
		/// Nejde o Dictionary, jde tedy možno mít více argumentů se shodným Key (nebo bez Key).
		/// </summary>
		public static List<KeyValuePair<string, string>> ParamList { get { return new List<KeyValuePair<string, string>>(Current._ParamList); } }
		#endregion
		#region Vyhledání služeb, vyhledání poskytovatelů služeb, tvorba poskytovatele
		/// <summary>
		/// Vrátí soupis TYPŮ poskytovatelů služeb daného druhu.
		/// Vstupem je typ služby = určení interface, který má služba (servisní třída) nabízet.
		/// Tento typ služby musí být jedním z těch, které byly předány do inicializace (v parametru metody Steward.AppInit().
		/// Výstupem je soupis typů konkrétních poskytovatelů této služby.
		/// </summary>
		/// <param name="service">Typ služby, například typeof(IGraphElementPainter)</param>
		/// <returns></returns>
		public static IEnumerable<Type> GetServiceTypeList(Type service)
		{
			return Current._GetServiceTypeList(service);
		}
		/// <summary>
		/// Vrátí soupis SKUTEČNÝCH OBJEKTŮ poskytovatelů služeb daného druhu.
		/// Vstupem je typ služby = určení interface, který má služba (servisní třída) nabízet.
		/// Tento typ služby musí být jedním z těch, které byly předány do inicializace (v parametru metody Steward.AppInit().
		/// Výstupem je soupis objektů konkrétních poskytovatelů této služby.
		/// </summary>
		/// <typeparam name="S">Typ služby, například IGraphElementPainter</typeparam>
		/// <param name="cached">Příznak, zda je možno číst objekty z cache</param>
		/// <returns>Soupis objektů</returns>
		public static IEnumerable<S> GetServiceObjectList<S>(bool cached)
		{
			return Current._GetServiceObjectList<S>(cached);
		}
		/// <summary>
		/// Vrátí objekt jednoho konkrétního poskytovatele služby daného druhu.
		/// Vstupem je typ služby = určení interface, který má služba (servisní třída) nabízet.
		/// Tento typ služby musí být jedním z těch, které byly předány do inicializace (v parametru metody Steward.AppInit().
		/// Výstupem je soupis objektů konkrétních poskytovatelů této služby.
		/// </summary>
		/// <typeparam name="S">Typ služby, například IGraphElementPainter</typeparam>
		/// <param name="providerType">Typ konkrétního poskytovatele služby, například typeof(PainterGanttRectangleCls), či spíš typ načtený z metody GetServiceTypeList()</param>
		/// <param name="cached">Příznak, zda je možno číst objekt z cache</param>
		/// <returns>Konkrétní poskytovatel anebo null</returns>
		public static S GetServiceObject<S>(Type providerType, bool cached)
		{
			return Current._GetServiceObject<S>(providerType, cached);
		}
		#endregion
		#region Funkce - načtení menu pro konkrétní situaci, 
		/// <summary>
		/// Pro daný klíč najde a vrátí definici položek kontextového menu.
		/// Klíčem je typ grafu, třída řádku a dat a grafický typ elementu.
		/// Definice menu je soupis vhodných nabídek pro danou situaci.
		/// Vhodné nabídky systém vybírá z dostupných poskytovatelů služby typu IFunctionMenuItem, 
		/// a to pomocí jejich metody GetMenuSuitableFor().
		/// Při příštím požadavku na získání menu pro identickou situaci je seznam položek již vrácen z paměti.
		/// </summary>
		/// <param name="graphMode">Typ grafu</param>
		/// <param name="menuKind">Prostor, kde se kliklo</param>
		/// <param name="elementDrawType">Druh elementu</param>
		/// <param name="rowClassNumber">Číslo třídy řádku</param>
		/// <param name="elementClassNumber">Číslo třídy elementu</param>
		/// <returns></returns>
		public static FunctionGroupCls FunctionGetMenu(RowGraphMode graphMode, FunctionMenuItemAreaType menuKind, GraphElementShapeType elementShapeType, int rowClassNumber, int elementClassNumber)
		{
			FunctionMenuItemKey menuKey = new FunctionMenuItemKey(graphMode, menuKind, elementShapeType, rowClassNumber, elementClassNumber);
			return Current._FunctionGetMenu(menuKey);
		}
		/// <summary>
		/// Pro daný klíč najde a vrátí definici položek kontextového menu.
		/// Klíčem je typ grafu, třída řádku a dat a grafický typ elementu.
		/// Definice menu je soupis vhodných nabídek pro danou situaci.
		/// Vhodné nabídky systém vybírá z dostupných poskytovatelů služby typu IFunctionMenuItem, 
		/// a to pomocí jejich metody GetMenuSuitableFor().
		/// Při příštím požadavku na získání menu pro identickou situaci je seznam položek již vrácen z paměti.
		/// </summary>
		/// <param name="menuKey">Klíč situace, pro kterou se hledá menu</param>
		/// <returns></returns>
		public static FunctionGroupCls FunctionGetMenu(FunctionMenuItemKey menuKey)
		{
			return Current._FunctionGetMenu(menuKey);
		}
		#endregion
		#endregion
		#region STATIC SLUŽBY - standardní statické metody bez použití instančního objektu
		/// <summary>
		/// Vyčistí paměť
		/// </summary>
		public static void MemoryGcFinish()
		{
			long memPrev = System.GC.GetTotalMemory(false);
			System.GC.Collect();
			long memAftN = System.GC.GetTotalMemory(true);

			long disposed = memPrev - memAftN;
		}
		/// <summary>
		/// Metoda zjistí, zda daný typ (dataType) implementuje daný interface.
		/// Oba typy jsou předané jako proměnné, to je důvod existence této metody.
		/// </summary>
		/// <param name="dataType">Typ testované třídy</param>
		/// <param name="interfaceType">Typ interface, které hledáme</param>
		/// <returns>true: testovaný typ implementuje daný interface</returns>
		public static bool TestTypeImplementInterface(Type dataType, Type interfaceType)
		{
			Type[] inf = dataType.FindInterfaces(delegate(Type t, object data) { return (t.FullName == ((Type)data).FullName); }, interfaceType);
			return (inf.Length > 0);
		}
        /// <summary>
        /// Vrátí první not null hodnotu z daného soupisu
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="values"></param>
        /// <returns></returns>
        public static T FirstNotNull<T>(params T[] values)
        {
            foreach (T value in values)
            {
                if (value != null) return value;
            }
            return default(T);
        }
		#endregion
        #region STATIC SLUŽBY - spouštění metod v try-catch bloku
        /// <summary>
        /// Vytvoří a otevře dané okno v bloku try-catch, případnou chybu zobrazí a utopí.
        /// Lze použít i s delegátem na anonymní metodu, která se vykoná s inicializovaným oknem před jeho otevřením.
        /// Typicky: 
        /// Steward.OpenFormInTryCatch(typeof(NašeOkno), "Chybová hláška %0", delegate { akce s oknem; })  (kde %0 v chybové hlášce se nahradí textem chyby).
        /// Metoda vrací 
        /// </summary>
        /// <param name="formType">Typ okna</param>
        /// <param name="errMsg"></param>
        /// <param name="action">Akce volaná před otevřením okna.
        /// Lze zadat buď jako lambda výraz (s parametrem form, bez návratové hodnoty), nebo jako delegáta: delegate(Form form) { akce1; akce2; akce3; }.
        /// Tato akce je rovněž zabalena v try-catch bloků, případná chyba bude odchycena a nahlášena.</param>
        public static DialogResult OpenDialogFormInTryCatch(Type formType, string errMsg, Action<Form> action)
        {
            DialogResult result = DialogResult.None;
            try
            {
                using (Form form = Steward.ObjectGenerator<Form>(formType, false))
                {
                    if (action != null)
                        action(form);
                    result = form.ShowDialog(Steward.MainForm);
                }
            }
            catch (Exception exc)
            {
                result = DialogResult.None;
                Dialog.ShowError(exc, errMsg.Replace("%0", exc.Message));
            }
            return result;
        }
        /// <summary>
        /// Vykoná daný blok kódu v bloku try-catch, případnou chybu zobrazí a utopí.
        /// Typicky se používá společně s delegátem na anonymní metodu:
        /// Steward.ExecuteInTryCatch(delegate { vlastní akce; }, "Chybová hláška %0")  (kde %0 v chybové hlášce se nahradí textem chyby).
        /// Pro otevření okna se lépe hodí metoda Steward.OpenDialogFormInTryCatch() nebo Steward.OpenNonModalFormInTryCatch().
        /// </summary>
        /// <param name="action"></param>
        /// <param name="errMsg"></param>
		public static void ExecuteInTryCatch(Action action, string errMsg)
		{
			try
			{
				action();
			}
			catch (Exception exc)
			{
				Dialog.ShowError(exc, errMsg.Replace("%0", exc.Message));
			}
		}
        #endregion
        #region NORIS PASSWORD
        /// <summary>
		/// Rozšifruje Noris heslo. 
		/// Nelze rozšifrovat otisk (signaturu).
		/// </summary>
		/// <param name="code">šifrovaný text, nikoli signatura</param>
		/// <returns></returns>
		public static string NorisPasswordDecrypt(string code)
		{
			if (String.IsNullOrEmpty(code)) return String.Empty;
			string resource = System.IO.Path.Combine(ExecutablePath, "Noris.Resources");
			return Noris.Schedule.Support.Core.NorisCrypt.Decrypt(code, resource);
		}
		/// <summary>
		/// Šifrování hesla pomocí prostředků Norisu.
		/// Vrácený text obsahuje plnohodnotně uložený vstupní text, a lze jej poté dešifrovat metodou Decrypt.
		/// </summary>
		/// <param name="text">čitelný text</param>
		/// <returns></returns>
		public static string NorisPasswordEncrypt(string text)
		{
			if (String.IsNullOrEmpty(text)) return String.Empty;
			string resource = System.IO.Path.Combine(ExecutablePath, "Noris.Resources");
			return Noris.Schedule.Support.Core.NorisCrypt.Encrypt(text, resource);
		}
		/// <summary>
		/// Vygeneruje otisk textu, který neobsahuje vlastní text ale jeho autentický otisk.
		/// Opakované volání vygeneruje pro stejný text jiný otisk.
		/// Z otisku nelze dešifrovat původní text.
		/// Vrácený otisk lze pouze použít pro porovnání pomocí metody NorisPasswordCompareSignature()
		/// s textem nově zadaným, zda odpovídá danému otisku.
		/// </summary>
		/// <param name="text">čitelný text</param>
		/// <returns></returns>
		public static string NorisPasswordCreateSignature(string text)
		{
			if (String.IsNullOrEmpty(text)) return String.Empty;
			string resource = System.IO.Path.Combine(ExecutablePath, "Noris.Resources");
			return Noris.Schedule.Support.Core.NorisCrypt.CreateSignature(text, resource);
		}
		/// <summary>
		/// Porovná shodu daného textu s šifrovaným otiskem.
		/// </summary>
		/// <param name="text">čitelný text</param>
		/// <param name="signature">signatura, nikoli zašifrovaný text</param>
		/// <returns></returns>
		public static bool NorisPasswordCompareSignature(string text, string signature)
		{
			if (String.IsNullOrEmpty(text) && String.IsNullOrEmpty(signature)) return true;
			if (String.IsNullOrEmpty(text) || String.IsNullOrEmpty(signature)) return false;
			string resource = System.IO.Path.Combine(ExecutablePath, "Noris.Resources");
			return Noris.Schedule.Support.Core.NorisCrypt.CompareSignature(text, signature, resource);
		}
		#endregion
		#region STATIC METODY APLIKACE (RunGUI(), MainForm, Show...)
		/// <summary>
		/// Spustí GUI aplikace (main okno)
		/// </summary>
        public static void RunGUI(Type mainFormType)
        {
            RunGUI(mainFormType, null);
        }
		/// <summary>
		/// Spustí GUI aplikace (main okno), ale před tím spustí danou akci a předá jí připravený Form.
		/// </summary>
		public static void RunGUI(Type mainFormType, Action<Form> runOnCreate)
		{
			try
			{
				object mainForm = System.Activator.CreateInstance(mainFormType);
				if (!(mainForm is Form))
					return;
				Form form = mainForm as Form;
                form.ShowInTaskbar = true;
				ApplicationContext appc = new ApplicationContext(form);
				Current._GuiThread = System.Threading.Thread.CurrentThread;
				Current._GuiThread.Name = "Main GUI Thread";
				MainForm = form;
                if (runOnCreate != null)
                    runOnCreate(form);   // Akce před otevřením okna
				Application.Run(appc);   // Tady stojí toto vlákno až do skončení aplikace. V případě vizuální akce ji zdejší vlákno obsluhuje.
			}
			catch (Exception exc)
			{
				Dialog.ShowError(exc, MessageInfo.Get("Chyba při běhu aplikace."), false);
			}
			finally
			{
                Current._AppExit();
				MainForm = null;
				Current._GuiThread = null;
			}
		}
		public static void SetSupportPathAsDefault()
		{
			Assembly thisAssembly = Assembly.GetExecutingAssembly();
			string fn = thisAssembly.FullName;
			Module[] ms = thisAssembly.GetLoadedModules();
			string dllPath = System.IO.Path.GetDirectoryName(ms[0].FullyQualifiedName);
			System.IO.Directory.SetCurrentDirectory(dllPath);
		}
		/// <summary>
		/// Nastaví adresář, kde je uložena aplikace, jako defaultní
		/// </summary>
		public static void SetExePathAsDefault()
		{
			// string defPath = System.IO.Directory.GetCurrentDirectory();
			string exePath = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
			System.IO.Directory.SetCurrentDirectory(exePath);
		}
		private Form _MainForm;
		private ICallBackTarget _CallBackTarget;
		/// <summary>
		/// Thread GUI
		/// </summary>
		public static Thread GuiThread { get { return Current._GuiThread; } } 
		private Thread _GuiThread;
		/// <summary>
		/// Obsahuje true, pokud aplikace běží v Debug režimu (je spuštěna z Visual studia)
		/// </summary>
		public static bool DebugMode
		{
			get
			{
				if (!Current._DebugMode.HasValue)
					Current._DetectDebugMode();
				bool? debugMode = Current._DebugMode;
				return (debugMode.HasValue && debugMode.Value);
			}
		}
		private void _DetectDebugMode()
		{
			this._DebugMode = System.Diagnostics.Debugger.IsAttached;
		}
		private bool? _DebugMode;
		#endregion
		#region FORM SIZE and POSITION
		/// <summary>
		/// Najde parametry (pozici a velikost) daného okna v paměti a aplikuje je na okno (volá se při jeho otevírání).
		/// Typicky tuto metodu volá bázová třída okna, takže konkrétní potomek ji volat nemusí.
		/// </summary>
		/// <param name="form"></param>
		public static void WindowLoadBounds(Form form)
		{
			Type formType = form.GetType();
			string key = "FormBounds." + formType.Namespace + "." + formType.Name;
			System.Drawing.Rectangle bounds;
			if (Current._AppConfig.UserData.TryGetData<System.Drawing.Rectangle>(key, out bounds))
			{
				form.Bounds = bounds;
			}
		}
		/// <summary>
		/// Uloží parametry (pozici a velikost) daného okna do paměti pro příští použití.
		/// Typicky tuto metodu volá bázová třída okna, takže konkrétní potomek ji volat nemusí.
		/// </summary>
		/// <param name="form"></param>
		public static void WindowSaveBounds(Form form)
		{
			Type formType = form.GetType();
			string key = "FormBounds." + formType.Namespace + "." + formType.Name;
			Current._AppConfig.UserData.SetData(key, form.Bounds);
		}
		#endregion
		#region COWLEY - NON INTERACTIVE RUN, internal podpora ze strany Stewarda (public metody jsou ve třídě Cowley). Steward je pouze správcem fondu vláken (Current._ThreadCowleyDict)
		/// <summary>
		/// Obsah parametru "Cowley=???" (předaný při spouštění programu).
		/// Pokud parametr nebyl předán, je zde null.
		/// Pokud byl předán parametr bez obsahu, je zde empty string.
		/// </summary>
		/// <returns></returns>
		public static string CowleyIdFromParams
		{
			get
			{
				string cowleyId = ParamGet("Cowley", true);
				return cowleyId;
			}
		}
		/// <summary>
		/// Obsahuje true, pokud v parametrech spouštění aplikace je předán parametr "Cowley=" obsahující neprázdný identifikátor funkce
		/// </summary>
		public static bool CowleyExistsFromParams
		{
			get
			{
				return (!String.IsNullOrEmpty(CowleyIdFromParams));
			}
		}
		/// <summary>
		/// Spustí danou funkci Cowley
		/// </summary>
		/// <param name="cowleyType">Type funkce (podle Type lze vygenerovat nový objekt)</param>
		internal static void CowleyRun(Type cowleyType, ICallBackTarget callBackTarget)
		{
			Current._CowleyRun(cowleyType, callBackTarget);
		}
		/// <summary>
		/// Zaloguje se podle parametrů, pokud to nejde ohlásí chybu (Throw.InputError()).
		/// </summary>
		internal static void CowleyLogin()
		{
			if (!Current._CowleyLogin())
				Throw.InputError(MessageInfo.Get("Podle předaných parametrů není možno se připojit k datům."));
		}
		/// <summary>
		/// Ověří, zda aktuální uživatel má právo na spouštění dané funkce. Pokud ne, ohlásí chybu (Throw.InputError()).
		/// </summary>
		internal static void CowleyCheckPermission(int functionNumber)
		{
			if (!Steward.DataReady)
				Throw.InputError(MessageInfo.Get("Systém není připojen k databázi."));

			string functionName;
			if (!Current._GetFunctionPermission(Steward.CurrentUser, functionNumber, out functionName))
			{
				Throw.InputError(MessageInfo.Get("Přihlášený uživatel %0 nemá v databázi %1 právo ke spouštění funkce %2: %3.",
					Steward.CurrentUser.Login + ": " + Steward.CurrentUser.OsobaNazev,
					Steward.AppConfig.CurrentProfile.DataSource + "." + Steward.AppConfig.CurrentProfile.InitialCatalog,
					functionNumber, functionName));
			}
		}
		/// <summary>
		/// Odloguje se od profilu.
		/// </summary>
		internal static void CowleyLogout()
		{
			Steward.Login(null, null, true);
		}
		/// <summary>
		/// Spustí danou funkci Cowley jako novou funkci v novém vláknu.
		/// Funkce + vlákno je uloženo do fondu Current._ThreadCowleyList, a pak je spuštěno (StartThread()).
		/// Funkce ve svém těle může volat progres pomocí volání metod Cowley.DoProgress() (statická metoda), kde se zajistí vyhledání odpovídajícího threadu.
		/// Funkce tedy nemůže volat progres z jiného threadu, než ve kterém byla spuštěna (a když by si založila další thread, pak musí progres volat ze svého main threadu).
		/// </summary>
		/// <param name="cowleyType">Type funkce (podle Type lze vygenerovat nový objekt)</param>
		/// <param name="callBackTarget">Target pro předávání eventů o progresu a o dokončení</param>
		private void _CowleyRun(Type cowleyType, ICallBackTarget callBackTarget)
		{
			ICowley cwl = Steward.GetServiceObject<ICowley>(cowleyType, false);            // Vytvořím si nový objekt Cowley tohoto typu (nebudu používat předaný objekt, mohl by už být hodně ojetý)

			ThreadCowley tc = new ThreadCowley(cwl, callBackTarget);
			Current._ThreadCowleyDict.Add(tc.ThreadId, tc);

			tc.StartThread();
		}
		/// <summary>
		/// Metoda vrátí objekt ThreadCowley, který je příslušný tomu threadu, který volá.
		/// Pokud pro volající thread neexistuje ThreadCowley, pak vrátí null.
		/// </summary>
		/// <returns></returns>
		internal static ThreadCowley CowleyThreadFind()
		{
			int thId = System.Threading.Thread.CurrentThread.ManagedThreadId;
			ThreadCowley tc;
			if (Current._ThreadCowleyDict.TryGetValue(thId, out tc))
				return tc;
			return null;
		}
		/// <summary>
		/// Cowley skončil definitivně (ať už bez chyby nebo s chybou).
		/// Je úkolem Stewarda najít thread toho Cowleyho v jeho fondu a odebrat jej z něj.
		/// Oznámení o ukončení běhu threadu do CallBackTargetu posílá ThreadCowley před tím, než volá tuto metodu.
		/// </summary>
		internal static void CowleyThreadRemove(ThreadCowley tc)
		{
			if (tc != null && Current._ThreadCowleyDict.ContainsKey(tc.ThreadId))
				Current._ThreadCowleyDict.Remove(tc.ThreadId);
		}
		/// <summary>
		/// Pokusí se zalogovat, anebo vrátí false.
		/// </summary>
		/// <returns></returns>
		private bool _CowleyLogin()
		{
			bool isLogged = false;
			NorisUserCls user = null;
			ConnectProfile dbProfile = null;

			// A) Login při spouštění z Norisovské funkce (parametry: Us, Sc/Sd, D):
			if (!isLogged)
				isLogged = this._LoginByGreenCwl(out user, out dbProfile);

            // X) AsLcs:
            if (!isLogged)
                isLogged = this._LoginAsLcs(out user, out dbProfile);

			// B) Login podle parametrů N-P-D:
			if (!isLogged)
				isLogged = this._LoginByParams(out user, out dbProfile);

			if (!isLogged)
				return false;

			// Přihlásit se:
			Steward.Login(dbProfile, user, true);

			return Steward.DataReady;
		}
		/// <summary>
		/// Soupis pracujících threadů pro funkce na pozadí
		/// </summary>
		private Dictionary<int, ThreadCowley> _ThreadCowleyDict;
		#endregion
        #region PROXY NA WinReg
        /// <summary>
        /// Příznak, zda je možno používat Windows registry
        /// </summary>
        public static bool UseWinReg { get { return Current._UseWinReg; } }
        /// <summary>
        /// Příznak, zda je možno používat Windows registry a zda jsou připravené (mají nastaven RootPath)
        /// </summary>
        public static bool WinRegPrepared { get { return (UseWinReg ? WinReg.Prepared : false); } }
		/// <summary>
		/// Nastaví výchozí (defaultní) aktuální základní cestu do registru. Současně ji aktivuje jako aktuální.
		/// Tuto metodu je třeba volat dříve, než se začne používat systém WinReg. Bez nastavení defaultní cesty nebude systém pracovat.
		/// Na tuto cestu je možno se kdykoli vrátit vyvoláním metody RestoreRootDefaultFolder() - bez parametrů, 
		/// defaultní hodnota je uložena ve třídě WinReg.
		/// Základní cestu je možno dočasně změnit metodou SetRootFolder(string folder).
		/// </summary>
		/// <param name="defaultFolder">Defaultní základní adresář. Typicky: "Software\Firma\Produkt\Verze", bez lomítek na začátku a na konci, se zpětnými lomítky.</param>
		public static void WinRegSetRootDefaultFolder(string defaultFolder)
        {
            if (UseWinReg)
                WinReg.SetRootDefaultFolder(defaultFolder);
        }
		/// <summary>
		/// Načte soupis datových klíčů z dané složky Win registru.
		/// </summary>
		/// <param name="folder">Složka, může být "" (tj. aplikační root, ne registrový root)</param>
		/// <returns>Seznam názvů hodnot v dané složce</returns>
        public static List<string> WinRegGetValueNames(string folder)
        {
            if (UseWinReg)
                return WinReg.GetValueNames(folder);
            return new List<string>();
        }
        /// <summary>
		/// Načte řetězec z Windows registru
		/// </summary>
		/// <param name="folder">Složka, může být "" (tj. aplikační root, ne registrový root)</param>
		/// <param name="keyName">Název klíče, musí být zadán</param>
		/// <param name="defValue">Default hodnota</param>
		/// <returns>Nalezený string / Default hodnota</returns>
        public static string WinRegReadString(string folder, string keyName, string defValue)
        {
            if (UseWinReg)
                return WinReg.ReadString(folder, keyName, defValue);
            return defValue;
        }
        /// <summary>
		/// Do registru zapíše hodnotu typu string.
		/// </summary>
		/// <param name="folder">Složka, může být ""</param>
		/// <param name="keyName">Název hodnoty</param>
		/// <param name="value">Hodnota, může být i null.</param>
        public static void WinRegWriteString(string folder, string keyName, string value)
        {
            if (UseWinReg)
                WinReg.WriteString(folder, keyName, value);
        }
		/// <summary>
		/// Načte řetězec z Windows registru
		/// </summary>
		/// <param name="folder">Složka, může být "" (tj. aplikační root, ne registrový root)</param>
		/// <param name="keyName">Název klíče, musí být zadán</param>
		/// <param name="defValue">Default hodnota</param>
		/// <returns>Nalezený string / Default hodnota</returns>
        public static string WinRegReadStringCrypt(string folder, string keyName, string defValue)
		{
            if (UseWinReg)
                return WinReg.ReadStringCrypt(folder, keyName, defValue);
            return defValue;
		}
		/// <summary>
		/// Do registru zapíše hodnotu typu string, zakryptovanou.
		/// </summary>
		/// <param name="folder">Složka, může být ""</param>
		/// <param name="keyName">Název hodnoty</param>
		/// <param name="value">Hodnota, může být i null.</param>
        public static void WinRegWriteStringCrypt(string folder, string keyName, string value)
        {
            if (UseWinReg)
                WinReg.WriteStringCrypt(folder, keyName, value);
        }
        /// <summary>
        /// Načte číslo Int32 z Windows registru
        /// </summary>
        /// <param name="folder">Složka, může být "" (tj. aplikační root, ne registrový root)</param>
        /// <param name="keyName">Název klíče, musí být zadán</param>
        /// <param name="defValue">Default hodnota</param>
        /// <returns>Nalezený string / Default hodnota</returns>
        public static int WinRegReadInt32(string folder, string keyName, int defValue)
        {
            if (UseWinReg)
                return WinReg.ReadInt32(folder, keyName, defValue);
            return defValue;
        }
        /// <summary>
        /// Do registru zapíše hodnotu typu Int32.
        /// </summary>
        /// <param name="folder">Složka, může být ""</param>
        /// <param name="keyName">Název hodnoty</param>
        /// <param name="value">Hodnota.</param>
        public static void WinRegWriteInt32(string folder, string keyName, int value)
        {
            if (UseWinReg)
                WinReg.WriteInt32(folder, keyName, value);
        }
        /// <summary>
        /// Načte hodnotu Bool z Windows registru
        /// </summary>
        /// <param name="folder">Složka, může být "" (tj. aplikační root, ne registrový root)</param>
        /// <param name="keyName">Název klíče, musí být zadán</param>
        /// <param name="defValue">Default hodnota</param>
        /// <returns>Nalezená hodnota bool / Default hodnota (false)</returns>
        public static bool WinRegReadBool(string folder, string keyName, bool defValue)
        {
            if (UseWinReg)
                return WinReg.ReadBool(folder, keyName, defValue);
            return defValue;
        }
        /// <summary>
        /// Do registru zapíše hodnotu typu Int32.
        /// </summary>
        /// <param name="folder">Složka, může být ""</param>
        /// <param name="keyName">Název hodnoty</param>
        /// <param name="value">Hodnota.</param>
        public static void WinRegWriteBool(string folder, string keyName, bool value)
        {
            if (UseWinReg)
                WinReg.WriteBool(folder, keyName, value);
        }
        /// <summary>
        /// Kořenová cesta k Windows registrům této aplikace
        /// </summary>
        public const string WINREG_ROOTPATH = @"Software\DJsoft\WinApsClient";



        private bool _UseWinReg
        {
            get
            {
                if (!this.__UseWinReg.HasValue)
                    this.__UseWinReg = this._DetectUseWinReg();
                return this.__UseWinReg.Value;
            }
        }
        private bool? __UseWinReg = null;
        /// <summary>
        /// Zjistí, zda je možno používat Windows registry
        /// </summary>
        /// <returns></returns>
        private bool? _DetectUseWinReg()
        {
            bool result = false;
            if (!CowleyExistsFromParams)
            {   // Pokud není zadán parametr Cowley (=tedy jedeme v interaktivním režimu), pak můžeme používat Windows registry:
                try
                {
                    WinReg.SetRootDefaultFolder(@"Software");  // Pokud uživatel nemá právo na Windows registry, dojde k chybě a nenastaví se result = true.
                    result = true;                             // Pokud nedojde k chybě, jsou Windows registry použitelné.
                }
                catch (Exception)
                { }
            }
            return result;
        }
        #endregion
        #region STATIC PROPERTY
        /// <summary>
		/// Příznak, že je korektně inicializována aplikace, a je možno používat ostatní metody Stewarda.
		/// Na true se nastaví provedením metody AppInit(), nikoli pouhým instancováním Stewarda !!!
		/// </summary>
		public static bool AppReady { get { return Current._AppReady; } }
		/// <summary>
		/// Reference na Main okno aplikace.
		/// </summary>
		public static Form MainForm
		{
			get
			{
				Form mainForm = Current._MainForm;
				if (mainForm == null) return null;
				if (mainForm.IsDisposed) return null;
				return mainForm;
			}
			private set
			{ 
				Current._MainForm = value;

				if (value != null && value is ICallBackTarget)
					Current._CallBackTarget = value as ICallBackTarget;
				else
					Current._CallBackTarget = null;
			} 
		}
		/// <summary>
		/// Reference na Main okno aplikace.
		/// Pokud je ale vytvořené v jiném threadu, vrací null.
		/// </summary>
		public static Form MainFormCurrentThread
		{
			get
			{
				Form mainForm = Current._MainForm;
				if (mainForm == null) return null;
				if (mainForm.IsDisposed) return null;
				if (mainForm.InvokeRequired) return null;
				return mainForm;
			}
			private set { Current._MainForm = value; }
		}
        /// <summary>
        /// Zobrazí daný Form v threadu MainFormu (=GUI thread), a toto okno (childForm) zobrazí s parametrem owner = MainForm.
        /// </summary>
        /// <param name="childForm"></param>
        public static void ShowFormInGuiThread(Form childForm)
        {
            System.Windows.Forms.Form mainForm = MainForm;
            if (mainForm == null || !mainForm.IsMdiContainer) return;

            if (mainForm.InvokeRequired)
                mainForm.BeginInvoke(new Action<Form, Form>(_ShowFormInGuiThread), mainForm, childForm);
            else
                _ShowFormInGuiThread(mainForm, childForm);
        }
        private static void _ShowFormInGuiThread(Form mainForm, Form childForm)
        {
            childForm.ShowInTaskbar = false;
            childForm.Show(mainForm);
        }
        /// <summary>
		/// Příznak, že existuje reference na objekt (ICallBackTarget)CallBackTarget, který je volán při změně stavu na pozadí.
		/// </summary>
		public static bool CallBackTargetExists { get { return (CallBackTarget != null); } }
		/// <summary>
		/// Reference na Main form formulář, pokud je možno jej volat jako ICallBackTarget
		/// </summary>
		public static ICallBackTarget CallBackTarget
		{
			get 
			{
				if (MainForm == null) return null;         // Pokud už není k dispozici MainForm, pak nemlže být ani ICallBackTarget (protože to je MainForm, jen jinak typované).
				return Current._CallBackTarget;
			}
		}
		/// <summary>
		/// Zobrazí Form jako modální okno
		/// </summary>
		/// <param name="form"></param>
		public static void ShowFormDialog(Form form)
		{
			Form mainForm = MainFormCurrentThread; // MainForm;
			if (mainForm == null || !mainForm.Visible)
			{
				form.ShowInTaskbar = true;
				form.ShowDialog();
			}
			else
			{
				if (mainForm.InvokeRequired)
					mainForm.Invoke(new Action<Form>(_ShowFormDialogRun), form);
				else
					_ShowFormDialogRun(form);
			}
		}
		private static void _ShowFormDialogRun(Form form)
		{
			MainForm.AddOwnedForm(form);
			form.ShowDialog(MainForm);
		}
		/// <summary> { get; set; }
		/// Obsahuje informaci o aktuálně přihlášeném uživateli.
		/// </summary>
		public static NorisUserCls CurrentUser { get { return Current._CurrentUser; } set { Current._CurrentUser = value;} }
		/// <summary> { get; set; }
		/// Login, který naposledy uživatel použil pro přihlašování
		/// </summary>
		public static string LastLogin { get { return Current._AppConfig.LastLogin; } set { Current._AppConfig.LastLogin = value; } }
		/// <summary>
		/// Obsahuje aktuální aplikační konfiguraci.
		/// </summary>
		public static AppConfigCls AppConfig { get { return Current._AppConfig; } }
		/// <summary>
		/// Konfigurační data Norisu
		/// </summary>
		public static NorisConfigCls NorisConfig { get { return Current._NorisConfig; } }
		/// <summary>
		/// Obsahuje aktuální profil připojení k databázi.
		/// </summary>
		public static ConnectProfile Connect { get { return Current._AppConfig.CurrentProfile; } }
		/// <summary>
		/// zajistí připojení k danému profilu
		/// </summary>
		/// <param name="profile"></param>
		public static void ConnectToProfile(ConnectProfile profile)
		{
			Noris.Schedule.Support.Green.Repository.Reset();
			Noris.Schedule.Support.Sql.DataConnect.Disconnect();
			Current._AppConfig.CurrentProfile = profile;
		}
		/// <summary>
		/// Obsahuje true, pokud je aktuální ConnectProfile (Connect) zadán tak, aby mělo význam pokoušet se připojit k databázi.
		/// Tj. je vyplněný server, databáze. Pokud není vyplněno jméno UserID, pokusí se připojit přes Windows autentifikaci.
		/// Do property je možno setovat hodnotu = má význam při detekci chyby při Connect.Open() setovat false (profil není ready).
		/// <para>
		/// Obsahuje false, pokud s aktuálním profilem není možno se připojit.
		/// </para>
		/// </summary>
		public static bool ConnectReady { get { return Current._AppConfig.CurrentProfile.Correct && !DataConnect.CurrentConnect.ConnectFailed; } }
		/// <summary>
		/// Vrací true, pokud je daná definice spojení použitelná
		/// </summary>
		/// <param name="sqlConnection"></param>
		/// <returns></returns>
		public static bool IsConnectReady(System.Data.SqlClient.SqlConnection sqlConnection)
		{
			if (sqlConnection == null) return false;
			if (String.IsNullOrEmpty(sqlConnection.DataSource)) return false;
			if (String.IsNullOrEmpty(sqlConnection.Database)) return false;
			return true;
		}
		/// <summary>
		/// Aktuálně používaná verze dat.
		/// Nabídku verzí generuje datový zdroj pro konkrétní databázový profil, Schedule ji nabízí v toolbaru.
		/// Jakmile si uživatel vybere, uloží se nově vybraná verze dat do této property a odsud se předává do požadavků, 
		/// které se předávají do datového zdroje.
		/// Typ této property je object, protože konkrétní typ definuje datový zdroj, a datový zdroj na jeho hodnotu reaguje.
		/// Schedule nijak neřeší obsah tohoto objektu.
		/// Drobný rozdíl je v tom, že datový zdroj vrací StringTag, kde Text je popisek, a Tag je objekt s daty, 
		/// ale v této property se nachází už jen objekt s daty (z Tagu).
		/// </summary>
		public static object CurrentDataVersion { get { return Current._CurrentDataVersion; } set { Current._CurrentDataVersion = value; } }
		/// <summary>
		/// Příznak, že je korektně připraveno datové prostředí (vybrán profil), 
		/// za této podmínky lze volat požadavky na datové zdroje a řešit čtení dat do grafů.
		/// </summary>
		public static bool DataReady { get { return Current._DataReady; } set { Current._DataReady = value; } }
		/// <summary>
		/// Vrací adresář k aplikaci
		/// </summary>
		public static string ExecutablePath { get { return Current._ExecutablePath; } }
        /// <summary>
        /// Příznak, že aplikace je spuštěna s parametrem "ReadOnly=true", pak nelze data editovat ani ukládat
        /// </summary>
        public static bool RunReadOnly { get { return Current._GetRunReadOnly(); } }
        /// <summary>
        /// SessionId aktuální session
        /// </summary>
        public static string SessionId { get { return Current._SessionId; } }
        /// <summary>
        /// Vrátí (poprvé zjistí) hodnotu z parametrů (argumenty), zda je aplikace spuštěná s parametrem "ReadOnly=true".
        /// </summary>
        /// <returns></returns>
        private bool _GetRunReadOnly()
        {
            if (!_RunReadOnly.HasValue)
            {
                string readOnly = this._ParamGet("ReadOnly", true);
                this._RunReadOnly = (readOnly != null && readOnly == "true");
            }
            return this._RunReadOnly.Value;
        }
        private bool? _RunReadOnly = null;
		#endregion
		#region SINGLETON na svoji instanci
		/// <summary>
		/// Privátní přístup k instančním datům stewarda
		/// </summary>
		private static Steward Current
		{
			get
			{
				if (_Current != null) return _Current;
				lock (_LockerWrite)
				{
					if (_Current == null)
					{
                        Steward current = Steward.Create();
                        _Current = current;
						_Current._AppConfig.Init();
					}
				}
				return _Current;
			}
		}
		/// <summary>
		/// Statické úložiště pro instanci
		/// </summary>
		private static Steward _Current;
		/// <summary>
		/// Locker
		/// </summary>
		private static object _LockerWrite = new object();
		#endregion
		#region SPRÁVCE PARAMETRŮ APLIKACE
		/// <summary>
		/// Parametry spuštění.
		/// Key je hodnota před rovnítkem, Value je hodnota za rovnítkem.
		/// Pokud argument neobsahuje rovnítko, pak je Key prázdné.
		/// Nejde o Dictionary, jde tedy možno mít více argumentů se shodným Key (nebo bez Key).
		/// </summary>
		private List<KeyValuePair<string, string>> _ParamList;
		/// <summary>
		/// Uloží do sebe parametry předané při spouštění aplikace
		/// </summary>
		/// <param name="arguments"></param>
		private void _ParamStore(string[] arguments)
		{
			this._ParamList = new List<KeyValuePair<string,string>>();
			if (arguments == null) return;

			foreach (string argument in arguments)
			{
				string text = argument.Trim();
				int posE = text.IndexOf("=");              // Kde je rovnítko
				int posU = text.IndexOf("\"");             // Kde je uvozovka
				if (posE >= 0 && posU >= 0 && posU < posE) // Pokud je tam obojí, ale uvozovka je dřív než rovnítko, pak jako by rovnítko nebylo
					posE = -1;
				string key = String.Empty;
				string value = text;
                if (posE < 0)
                {
                    key = value;
                    value = "";
                }
                else if (posE == 0)
                {
                    value = text.Substring(1);
                }
                else if (posE > 0)
                {
                    key = text.Substring(0, posE).Trim();
                    value = text.Substring(posE + 1).Trim();
                }
				this._ParamList.Add(new KeyValuePair<string, string>(key, value));
			}
		}
		/// <summary>
		/// Metoda najde a vrátí první parametr spuštění, který odpovídá danému klíči.
		/// Pokud nenajde nic, vrací String.Empty
		/// </summary>
		/// <param name="key"></param>
		/// <param name="ignoreCase"></param>
		/// <returns></returns>
		private string _ParamGet(string key, bool ignoreCase)
		{
			return _ParamGet(new string[] { key }, ignoreCase);
		}
		/// <summary>
		/// Metoda najde a vrátí první parametr spuštění, který odpovídá danému klíči.
		/// Pokud nenajde nic, vrací null
		/// </summary>
		/// <param name="keyList"></param>
		/// <param name="ignoreCase"></param>
		/// <returns></returns>
		private string _ParamGet(string[] keyList, bool ignoreCase)
		{
			List<KeyValuePair<string, string>> values = _ParamsGet(keyList, ignoreCase);
			if (values.Count == 0) return null;
			return values[0].Value;
		}
		/// <summary>
		/// Metoda určí počet parametrů s daným klíčem.
		/// Lze využít pro detekci existence určitého parametru.
		/// </summary>
		/// <param name="keyList"></param>
		/// <param name="ignoreCase"></param>
		/// <returns>Počet odpovídajících parametrů</returns>
		private int _ParamCount(string[] keyList, bool ignoreCase)
		{
			List<KeyValuePair<string, string>> values = _ParamsGet(keyList, ignoreCase);
			return values.Count;
		}
		/// <summary>
		/// Metoda najde a vrátí všechny parametry spuštění, které odpovídají danému klíči.
		/// </summary>
		/// <param name="keyList"></param>
		/// <param name="ignoreCase"></param>
		/// <returns></returns>
		private List<KeyValuePair<string, string>> _ParamsGet(string[] keyList, bool ignoreCase)
		{
			List<KeyValuePair<string, string>> values = new List<KeyValuePair<string, string>>();
			foreach (KeyValuePair<string, string> param in _ParamList)
			{
				bool equal = false;
				if (keyList == null || keyList.Length == 0)
					equal = true;
				else
				{
					foreach (string key in keyList)
					{
						bool equalOne = (ignoreCase ? String.Equals(param.Key, key, StringComparison.OrdinalIgnoreCase) : String.Equals(param.Key, key, StringComparison.Ordinal));
						if (equalOne)
						{
							equal = true;
							break;
						}
					}
				}
				if (equal)
					values.Add(param);
			}
			return values;
		}
		#endregion
		#region TVORBA INSTANCE STEWARDA, TVORBA INSTANČNÍCH PROMĚNNÝCH
		/// <summary>
		/// Konstruktor je private, zvenku si stewarda nikdo nevyrobí
		/// </summary>
		private Steward() { }
		/// <summary>
		/// Privátní tvorba stevarda
		/// </summary>
		private static Steward Create()
		{
			Steward steward = new Steward();

			steward._CurrentUser = new NorisUserCls(false);
			steward._AppConfig = new AppConfigCls();
			steward._ServiceMaps = new Dictionary<Type, Dictionary<Type, object>>();
			steward._ServiceWriteLock = new object();
			steward._ThreadCowleyDict = new Dictionary<int, ThreadCowley>();

			return steward;
		}
		#endregion
		#region INSTANČNÍ PROMĚNNÉ
		/// <summary>
		/// Příznak, že je korektně inicializována aplikace, a je možno používat ostatní metody Stewarda.
		/// Na true se nastaví provedením metody AppInit(), nikoli pouhým instancováním Stewarda !!!
		/// </summary>
		private bool _AppReady;
		/// <summary>
		/// Úložiště dat o aktuálně přihlášeném uživateli.
		/// </summary>
		private NorisUserCls _CurrentUser;
		/// <summary>
		/// Aktuálně používaná verze dat
		/// </summary>
		private object _CurrentDataVersion;
		/// <summary>
		/// Příznak, že je korektně připraveno datové prostředí (vybrán profil).
		/// </summary>
		private bool _DataReady;
        /// <summary>
        /// SessionId aktuální session
        /// </summary>
        private string _SessionId
        {
            get
            {
                if (this.__SessionId == null)
                {
                    Random rnd = new Random();
                    this.__SessionId = rnd.Next(10000000, 99999999).ToString() + "162" + rnd.Next(10000000, 99999999).ToString();           // '2945896637247869072'     19 znaků
                }
                return this.__SessionId;
            }
        }
        private string __SessionId = null;
		/// <summary>
		/// Vrací adresář k aplikaci
		/// </summary>
		private string _ExecutablePath
		{
			get
			{
				if (this.__ExecutablePath == null)
					this.__ExecutablePath = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
				return this.__ExecutablePath;
			}
		}
		private string __ExecutablePath;
		/// <summary>
		/// Aplikační konfigurace
		/// </summary>
		private AppConfigCls _AppConfig;
		/// <summary>
		/// Konfigurační data Norisu, instanční property
		/// </summary>
		private NorisConfigCls _NorisConfig
		{
			get
			{
				if (__NorisConfig == null)
					this.__NorisConfig = NorisConfigCls.Current;
				return this.__NorisConfig;
			}
		}
		/// <summary>
		/// Konfigurační data Norisu, úložiště proměnné
		/// </summary>
		private NorisConfigCls __NorisConfig = null;
		/// <summary>
		/// Jednotné úložiště reflektovaných typů instalovaných služeb.
		/// Obsahuje :
		/// Key = typ služby (reprezentovaný typem interface)
		/// Value = seznam typů, které tuto službu poskytují 
		/// Seznam typů: Key = typ fyzické implementace, Value = cache instance služby nebo null.
		/// </summary>
		private Dictionary<Type, Dictionary<Type, object>> _ServiceMaps;
		/// <summary>
		/// Zámek na zápisy do _ServiceMaps
		/// </summary>
		private object _ServiceWriteLock;
		#endregion
		#region PRIVÁTNÍ INSTANČNÍ METODY
		#region SERVICES (System.Reflection): Load, Get, Activate, UnLoad
		/// <summary>
		/// Pomocí reflexe načte seznamy datových typů, které mohou sloužit jako Services (funkce, extendery, paintery, a kdovíco dalšího)
		/// </summary>
		private void _ServicesLoad()
		{
			// Příprava:
			lock (this._ServiceWriteLock)
			{
				Type[] serviceTypes = _ServicesFindInterfaceList();
				this._ServiceMaps = new Dictionary<Type, Dictionary<Type, object>>();
				this._ServicesLoadAdd(serviceTypes);
			}
		}
		/// <summary>
		/// Metoda najde všechny interface, které jsou potomkem IPlugin, a vrátí jejich seznam.
		/// Hledá pouze v namespace Noris.Schedule.Support.Services.
		/// </summary>
		/// <returns></returns>
		private Type[] _ServicesFindInterfaceList()
		{
			List<Type> serviceTypes = new List<Type>();
			Type iPlugin = typeof(IPlugin);

            IEnumerable<FileInfo> assemblyFiles = this._FindAssemblyFiles();
            foreach (FileInfo fileInfo in assemblyFiles)
			{
				Assembly assembly = Assembly.LoadFile(fileInfo.FullName);
                try
                {
                    Type[] assemblyTypes = assembly.GetTypes();                      // Soupis všech typů (třídy, struktury, interface, enumy, atd atd)
                    foreach (Type iType in assemblyTypes)
                    {
                        if (!iType.IsInterface) continue;

                        // Najdu všechny interface (do foundTypes), které daný datový typ implementuje:
                        Type[] foundTypes = iType.FindInterfaces(
                            delegate(Type t, object data)
                            {	// Anonymní delegát dostává postupně všechny interface, které datový typ implementuje.
                                // Má vrátit true, pokud máme konkrétní interface zařadit do výstupu:
                                return (t == data);
                            }, iPlugin);
                        if (foundTypes.Length > 0)
                            serviceTypes.Add(iType);
                    }
                }
                catch (System.Reflection.ReflectionTypeLoadException ex1)
                {
                    string msg = "";
                    foreach (Exception lex in ex1.LoaderExceptions)
                        msg += lex.Message + Environment.NewLine;
                    msg = @"V adresáři aplikace jsou chybně přeložené soubory." + Environment.NewLine + "Soubor: " + fileInfo.Name + " obsahuje chyby:" + Environment.NewLine + Environment.NewLine + msg;
                    Dialog.ShowError(ex1, msg);
                }
                catch (Exception ex2)
                {
                    string msg = @"V adresáři aplikace jsou uloženy soubory, které nejsou kompatibilní.
Při načítání programového souboru: " + fileInfo.FullName + @"
došlo k chybě: " + ex2.Message;
                    Dialog.ShowError(ex2, msg);
                }
            }

			return serviceTypes.ToArray();
		}
		/// <summary>
		/// Pomocí reflexe doplní existující seznam this._ServiceMaps o další služby a jejich poskytovatele.
		/// Zajistí načtení (reflexí fyzických knihoven) těch služeb, které jsou požadované, a přitom dosud nejsou načtené (ty už nedohledává).
		/// </summary>
		private void _ServicesLoadAdd(IEnumerable<Type> findServiceTypes)
		{
			Hashtable findTypes = new Hashtable();                               // Pomocný slovník, který si pro každý typ uloží příznak, zda typ implementuje dané interface
			foreach (Type service in findServiceTypes)
			{
				if (this._ServiceMaps.ContainsKey(service)) continue;            // Nonduplicitně!
				this._ServiceMaps.Add(service, new Dictionary<Type, object>());
				findTypes.Add(service, null);                                    // Pro QuickSearch v anonymním delegátovi
			}

			// Následující smyčka a rychlost:
			// Načtení mapy typů ze dvou knihoven (Schedule a Support) s celkovým počtem typů = 500 trvá 50 milisekund.
			IEnumerable<FileInfo> assemblyFiles = this._FindAssemblyFiles();
			foreach (FileInfo fileInfo in assemblyFiles)
			{
				Assembly assembly = Assembly.LoadFile(fileInfo.FullName);
                try
                {
                    Type[] assemblyTypes = assembly.GetTypes();                      // Soupis všech typů (třídy, struktury, interface, enumy, atd atd)

                    foreach (Type dataType in assemblyTypes)
                    {
                        // Přebíráme pouze class, kter= nejsou abstract:
                        if (!dataType.IsClass) continue;
                        if (dataType.IsAbstract) continue;

                        // Najdu všechny interface (do foundTypes), které daný datový typ implementuje:
                        Type[] foundTypes = dataType.FindInterfaces(
                            delegate(Type t, object data)
                            {	// Anonymní delegát dostává postupně všechny interface, které datový typ implementuje.
                                // Má vrátit true, pokud máme konkrétní interface zařadit do výstupu:
                                return (((Hashtable)data).ContainsKey(t));
                            }, findTypes);

                        // V poli foundTypes mám typy = interface, které implementuje datový typ, a na které jsme se ptali.
                        foreach (Type implementedInterface in foundTypes)
                            this._ServiceMaps[implementedInterface].Add(dataType, null);
                    }
                }
                catch (System.Reflection.ReflectionTypeLoadException ex1)
                {
                    string msg = "";
                    foreach (Exception lex in ex1.LoaderExceptions)
                        msg += lex.Message + Environment.NewLine;
                    msg = @"V adresáři aplikace jsou chybně přeložené soubory." + Environment.NewLine + "Soubor: " + fileInfo.Name + " obsahuje chyby:" + Environment.NewLine + Environment.NewLine + msg;
                    Dialog.ShowError(ex1, msg);
                }
                catch (Exception ex2)
                {
                    string msg = @"V adresáři aplikace jsou uloženy soubory, které nejsou kompatibilní.
Při načítání programového souboru: " + fileInfo.FullName + @"
došlo k chybě: " + ex2.Message;
                    Dialog.ShowError(ex2, msg);
                }
			}
		}
		/// <summary>
		/// Vrátí seznam souborů (FileInfo) DLL a EXE v adresáři programu aplikace.
		/// </summary>
		/// <returns></returns>
		private IEnumerable<FileInfo> _FindAssemblyFiles()
		{
			Assembly assembly = Assembly.GetExecutingAssembly();
			string path = Path.GetDirectoryName(assembly.Location);
			DirectoryInfo dirInfo = new DirectoryInfo(path);
			FileInfo[] files = dirInfo.GetFiles("*.*");

            string exeFile = System.IO.Path.GetFileName(Application.ExecutablePath).ToLower();
            string[] dllFiles = new string[] { "lcs.scheduler.", "lcs.report." };

			List<FileInfo> result = new List<FileInfo>();
			foreach (FileInfo fileInfo in files)
			{
				string ext = fileInfo.Extension.ToLower();
				bool isExecutable = (ext == ".dll" || ext == ".exe");
                if (!isExecutable) continue;

				string name = fileInfo.Name.ToLower();
                bool isOur = String.Equals(name, exeFile, StringComparison.InvariantCultureIgnoreCase);
                if (!isOur)
                    isOur = dllFiles.Any(f => name.StartsWith(f, StringComparison.InvariantCultureIgnoreCase));
                if (isOur)
                    result.Add(fileInfo);
			}

			result.Sort(this._FindAssemblyFilesSorter);

			return result;
		}
		private int _FindAssemblyFilesSorter(FileInfo a, FileInfo b)
		{
			int ap = _FindAssemblyFilesGetPriority(a.Name);
			int bp = _FindAssemblyFilesGetPriority(b.Name);
			return ap.CompareTo(bp);
		}

		private int _FindAssemblyFilesGetPriority(string name)
		{
			string lname = name.ToLower().Trim();
			if (lname == "lcs.scheduler.support.dll") return 1;
			if (lname.EndsWith(".exe")) return 5;
			return 3;
		}
		/// <summary>
		/// Najde a vrátí soupis TYPŮ poskytovatelů dané služby.
		/// Pokud požadovaná služba není v interním seznamu (nebyla přítomna při inicializaci),
		/// dohledá její poskytovatele nyní. Ztráta je jen časová. Příště už budou načteny i data pro tuto novou službu.
		/// </summary>
		/// <param name="service"></param>
		/// <returns></returns>
		private IEnumerable<Type> _GetServiceTypeList(Type service)
		{
			Dictionary<Type, object> providers = _GetServiceProviders(service);
			if (providers == null) return null;
			return new List<Type>(providers.Keys);
		}
		/// <summary>
		/// Najde a vrátí soupis INSTANCÍ poskytovatelů dané služby.
		/// Vrací soupis objektů, které jsou vytvořeny z typu konkrétního poskytovatele.
		/// Pokud požadovaná služba není v interním seznamu (nebyla přítomna při inicializaci),
		/// dohledá její poskytovatele nyní. Ztráta je jen časová. Příště už budou načteny i data pro tuto novou službu.
		/// </summary>
		/// <typeparam name="S">Typ služby = typ interface, který má služba implementovat. Vrací objekty typované tímto typem.</typeparam>
		/// <param name="cached">true = umožní číst objekty z cache / false = chce vždy čerstvé objekty. Pozor, čas!!!</param>
		/// <returns></returns>
		private IEnumerable<S> _GetServiceObjectList<S>(bool cached)
		{
			Type service = typeof(S);                      // Typ služby
			List<S> result = new List<S>();                // Výstup

			Dictionary<Type, object> providers = _GetServiceProviders(service);
			if (providers == null) return null;
			IEnumerable<Type> types = new List<Type>(providers.Keys);
			foreach (Type type in types)
			{
				if (cached)
				{	// Objekty z cache (anebo nové do cache a pak z cache ven):
					// Poznámka: v cache (providers) vždy existuje položka pro klíč (type), 
					//     protože seznam typů (types) jsme načetli z tohoto soupisu. 
					//     Důvod: Dictionary nelze procházet cyklem foreach() a přitom do ní vepisovat nová data.
					if (providers[type] == null)
						providers[type] = ObjectGenerator<S>(type);
					result.Add((S)providers[type]);
				}
				else
				{	// Objekty do výstupu vždy nové:
					result.Add(ObjectGenerator<S>(type));
				}
			}
			return result;
		}
		/// <summary>
		/// Vrátí objekt jednoho konkrétního poskytovatele služby daného druhu.
		/// Vstupem je typ služby = určení interface, který má služba (servisní třída) nabízet.
		/// Tento typ služby musí být jedním z těch, které byly předány do inicializace (v parametru metody Steward.AppInit().
		/// Výstupem je soupis objektů konkrétních poskytovatelů této služby.
		/// </summary>
		/// <typeparam name="S">Typ služby, například IGraphElementPainter</typeparam>
		/// <param name="providerType">Typ konkrétního poskytovatele služby, například typeof(PainterGanttRectangleCls), či spíš typ načtený z metody GetServiceTypeList()</param>
		/// <param name="cached">Příznak, zda je možno číst objekt z cache</param>
		/// <returns>Konkrétní poskytovatel anebo null</returns>
		private S _GetServiceObject<S>(Type providerType, bool cached)
		{
			Type service = typeof(S);                      // Typ služby

			// Najdu seznam služeb požadovaného typu S: (jde o soupis typů a cachovaných objektů providerů):
			Dictionary<Type, object> providers = _GetServiceProviders(service);
			if (providers == null) return default(S);

			// Najdu konkrétního providera, pokud nebude v seznamu tak ho ani nebudu vytvářet, nejspíš jde o jiný typ než je přípustné:
			object provider = null;
			if (!providers.TryGetValue(providerType, out provider)) return default(S);

			// Z cache / do cache anebo nový objekt:
			if (cached)
			{	// Objekty z cache (anebo nové do cache a pak z cache ven):
				// Poznámka: v cache (providers) vždy existuje položka pro klíč (type), 
				//     protože seznam typů (types) jsme načetli z tohoto soupisu. 
				//     Důvod: Dictionary nelze procházet cyklem foreach() a přitom do ní vepisovat nová data.
				if (provider == null)
				{
					provider = ObjectGenerator<S>(providerType);
					providers[providerType] = provider;
				}
			}
			else
			{	// Objekty do výstupu vždy nové:
				provider = ObjectGenerator<S>(providerType);
			}
			return (S)provider;
		}
		/// <summary>
		/// Metoda vrátí vnitřní dictionary pro danou službu.
		/// Pokud služba není načtena, načte ji.
		/// Vrací živou referenci do interní paměti.
		/// </summary>
		/// <param name="service"></param>
		/// <returns></returns>
		private Dictionary<Type, object> _GetServiceProviders(Type service)
		{
			Dictionary<Type, object> providers;
			// Máme už načtené providery dané služby:
			if (this._ServiceMaps.TryGetValue(service, out providers)) return providers;
			
			// Nemáme => můžeme služby zamknout, zkusit je donačíst, atd:
			lock (this._ServiceWriteLock)
			{
				if (this._ServiceMaps.TryGetValue(service, out providers)) return providers;
				this._ServicesLoadAdd(new Type[] { service });
				if (this._ServiceMaps.TryGetValue(service, out providers)) return providers;
			}

			return null;
		}
		/// <summary>
		/// Unload služeb.
		/// Uvolní služby z cache. Uvolní cache.
		/// Pokud je služba IDisposable, zavolá její Dispose.
		/// </summary>
		private void _ServicesUnLoad()
		{
			lock (this._ServiceWriteLock)
			{
				// Provedeme Dispose na všechny objekty, které to mají rády (ale na každém jen jedenkrát):
				List<object> disposedProviders = new List<object>();     // Reference na objekty, pro které jsme už zavolali Dispose
				foreach (Dictionary<Type, object> providers in this._ServiceMaps.Values)
				{
					foreach (object provider in providers)
					{
						if (provider == null) continue;                  // Pro tento typ nebyl provider cachován. Jdeme na další.
						if (provider is IDisposable)
						{	// Pokud je provider (krom jiného) i IDisposable:
							bool wasDisposed = disposedProviders.Exists(delegate(object w) { return (Object.ReferenceEquals(w, provider)); });
							if (!wasDisposed)
							{	// A pokud ještě na tomto konkrétním objektu nebyl proveden Dispose:
								disposedProviders.Add(provider);
								((IDisposable)provider).Dispose();
							}
						}
					}
				}

				// Odebereme vše z paměti, o zbytek se postará GC:
				this._ServiceMaps.Clear();
			}
		}
		/// <summary>
		/// Vrátí nově vytvořený objekt daného typu.
		/// Pokud se nepodaří, vrátí null.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="type"></param>
		/// <returns></returns>
        public static T ObjectGenerator<T>(Type type)
        {
            return ObjectGenerator<T>(type, true);
        }
		/// <summary>
		/// Vrátí nově vytvořený objekt daného typu.
		/// Pokud se nepodaří, vrátí null.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="type"></param>
		/// <returns></returns>
		public static T ObjectGenerator<T>(Type type, bool suppressException)
		{
			T result = default (T);
			try
			{
				object obj = System.Activator.CreateInstance(type, false);
				result = (T)obj;
			}
			catch (Exception exc)
			{
                if (suppressException)
                    result = default(T);
                else
                    throw;
			}
			return result;
		}
		/// <summary>
		/// Vrátí seznam objektů
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="typeList"></param>
		/// <returns></returns>
		public static List<T> ActivateTypes<T>(IEnumerable<Type> typeList)
		{
			List<T> result = new List<T>();
			foreach (Type type in typeList)
				result.Add((T)System.Activator.CreateInstance(type, false));
			return result;
		}
		#endregion
		#region AUDITLOVÁNÍ
		/// <summary>
		/// Zaznamená chybovou hlášku do standardního auditlogu.
		/// </summary>
		/// <param name="message"></param>
		/// <param name="exc"></param>
		public static void AuditInfo(string message)
		{
			_Audit(AuditRowType.Info, message, null, 0, 0, 0);
		}
        /// <summary>
        /// Zaznamená chybovou hlášku do standardního auditlogu.
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exc"></param>
        public static void AuditInfoWithContext(string message, int classNumber, int recordNumber, int folderNumber)
        {
            _Audit(AuditRowType.Info, message, null, classNumber, recordNumber, folderNumber);
        }
        /// <summary>
		/// Zaznamená chybovou hlášku do standardního auditlogu.
		/// </summary>
		/// <param name="message"></param>
		/// <param name="exc"></param>
		public static void AuditWarning(string message)
		{
            _Audit(AuditRowType.Info, message, null, 0, 0, 0);
		}
        /// <summary>
        /// Zaznamená chybovou hlášku do standardního auditlogu.
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exc"></param>
        public static void AuditWarningWithContext(string message, int classNumber, int recordNumber, int folderNumber)
        {
            _Audit(AuditRowType.Info, message, null, classNumber, recordNumber, folderNumber);
        }
        /// <summary>
        /// Do auditlogu uloží info o aktuálním stroji + procesu, pro případné detekce problémů
        /// </summary>
        public static void AuditCurrentProcess()
        {
            System.Diagnostics.Process thisProc = System.Diagnostics.Process.GetCurrentProcess();
            string fileName = thisProc.MainModule.FileName;

            string machine = SystemInformation.ComputerName;
            string domain = SystemInformation.UserDomainName;
            string domainApp = System.AppDomain.CurrentDomain.FriendlyName;
            bool ui = SystemInformation.UserInteractive;
            string user = SystemInformation.UserName;
            string message = "Machine=" + machine + "; Process=" + fileName + "; User=" + user + "; Domain=" + domain + "; AppDomain=" + domainApp + "; HasUI=" + (ui ? "Yes" : "No");
            _Audit(AuditRowType.Info, message, null, 0, 0, 0);

            message = _GetFileInfo(Application.ExecutablePath);
            if (message != null) _Audit(AuditRowType.Info, "Executable file: " + message, null, 0, 0, 0);
            message = _GetFileInfo(System.IO.Path.Combine(Steward.ExecutablePath, "Lcs.Scheduler.Planning.dll"));
            if (message != null) _Audit(AuditRowType.Info, "Planning library: " + message, null, 0, 0, 0);
        }
        /// <summary>
        /// Vrátí info o souboru
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private static string _GetFileInfo(string fileName)
        {
            if (String.IsNullOrEmpty(fileName)) return null;
            System.IO.FileInfo file = new FileInfo(fileName);
            if (!file.Exists) return fileName + " does not exists.";
            return fileName + ": Date=" + file.LastWriteTimeUtc.ToString("yyyy-MM-dd HH:mm:ss") + "UTC; Size=" + file.Length.ToString() + "B";
        }
        /// <summary>
        /// Zaznamená chybovou hlášku do standardního auditlogu.
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exc"></param>
        public static void Audit(AuditRowType auditType, string message)
        {
            _Audit(auditType, message, null, 0, 0, 0);
        }
        /// <summary>
		/// Zaznamená chybovou hlášku do standardního auditlogu.
		/// </summary>
		/// <param name="message"></param>
		/// <param name="exc"></param>
        public static void Audit(AuditRowType auditType, string message, Exception exc)
        {
            _Audit(auditType, message, exc, 0, 0, 0);
        }
        /// <summary>
        /// Zaznamená chybovou hlášku do standardního auditlogu.
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exc"></param>
        public static void AuditWithContext(AuditRowType auditType, string message, Exception exc, int classNumber, int recordNumber, int folderNumber)
        {
            _Audit(auditType, message, exc, classNumber, recordNumber, folderNumber);
        }
        /// <summary>
        /// Vrátí obsah titulkového řádku, včetně EOL
        /// </summary>
        /// <returns></returns>
        private string _AuditGetTitle()
        {
            return "DateTime\tIWE\tText/ErrorMessage\tSourceLine\tClassNumber\tRecordNumber\tFolderNumber" + Environment.NewLine;
        }
        /// <summary>
		/// Zaznamená chybovou hlášku do standardního auditlogu.
		/// </summary>
		/// <param name="message"></param>
		/// <param name="exc"></param>
        private static void _Audit(AuditRowType auditType, string message, Exception exc, int classNumber, int recordNumber, int folderNumber)
		{
            // Titulkový řádek by vypadal takhle (v této ukázce nejsou použity TABy, ale mezerníky):
            // "DateTime    IWE   Text/ErrorMessage    SourceLine   CN   RN   FN"
            //  Pokud není předán kontext = když je (classNumber == 0 || recordNumber == 0), pak ve sloupcích CN, RN, FN je empty. Ale TABy jsou obsaženy vždy v počtu 6 ks.
			string auditFile = Steward.AuditFile;

            StringBuilder content = new StringBuilder();

            // Vypsat úvodní text, pokud je nějaký připravený:
            if (Current._AuditTitle != null)
            {
                content.Append(Current._AuditTitle);
                Current._AuditTitle = null;
            }

            // Příprava:
			string tab = "\t";
			string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.f");
            string conempt = tab + tab + tab;                // Empty context: dává se do běžného řádku, když kontext není předán, a dává se do řádku s chybou vždy.
            string context = ((classNumber == 0 || recordNumber == 0) ? conempt :
                tab + classNumber.ToString() + tab + recordNumber.ToString() + tab + folderNumber.ToString());
			string text;

			// message:
			text = now + tab + 
				_AuditGetInfoChar(auditType) + tab + 
				_AuditGetText(message) + tab + 
				"" + 
                context + 
                Environment.NewLine;
            content.Append(text);

			// exception:
			Exception ex = exc;
			while (ex != null)
			{
				string line = AuditGetErrorRow(ex);
				text = now + tab +
					_AuditGetInfoChar(auditType) + tab +
					_AuditGetText(ex.Message) + tab +
					_AuditGetText(line) +
                    context + 
                    Environment.NewLine;

                content.Append(text);

				ex = ex.InnerException;
			}

            // Zapsat string do souboru (přidat na konec):
            System.IO.File.AppendAllText(auditFile, content.ToString(), Encoding.UTF8);
        }
        /// <summary>
        /// Vrací písmeno (N/I/W/C/B/D/E/S/Q/M) pro zadaný typ AuditRowType
        /// </summary>
        /// <param name="auditType"></param>
        /// <returns></returns>
		private static string _AuditGetInfoChar(AuditRowType auditType)
		{
			switch (auditType)
			{
				case AuditRowType.None: return "N";
				case AuditRowType.Info: return "I";
				case AuditRowType.Warning: return "W";
				case AuditRowType.Completed: return "C";
				case AuditRowType.BreakError: return "B";
				case AuditRowType.InputError: return "D";
				case AuditRowType.ApplicationError: return "E";
				case AuditRowType.CoreError: return "S";
				case AuditRowType.SqlError: return "Q";
				case AuditRowType.MsNetError: return "M";
			}
			return "";
		}
        /// <summary>
        /// Koriguje obsah textu tak, aby jej bylo možno vložit do řádku auditlogu.
        /// Pokud je na vstupu null, vrátí "".
        /// Nahradí TAB, CR, LF = space.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
		private static string _AuditGetText(string text)
		{
			if (String.IsNullOrEmpty(text)) return "";
			string result = text.Replace("\t", "  ");
			result = result.Replace("\r", "  ");
			result = result.Replace("\n", "  ");
			return result;
		}
		/// <summary>
		/// Najde a vrátí řádek programu, který pravděpodobně způsobil chybu
		/// </summary>
		/// <param name="stackTrace">Výpis Stacku v okamžiku chyby</param>
		/// <returns></returns>
		internal static string AuditGetErrorRow(Exception exc)
		{
			if (exc == null) return "";
			return AuditGetErrorRow(exc.StackTrace);
		}
		/// <summary>
		/// Najde a vrátí řádek programu, který pravděpodobně způsobil chybu
		/// </summary>
		/// <param name="stackTrace">Výpis Stacku v okamžiku chyby</param>
		/// <returns></returns>
		internal static string AuditGetErrorRow(string stackTrace)
		{
			if (stackTrace == null) return "";

			string[] stackLines = stackTrace.Split('\r', '\n');
			string errorPoint = "";
			string textAt = "";
			string textIn = "";

			foreach (string stackLine in stackLines)
			{
				string line = stackLine.Trim();
				if (line.Length == 0) continue;

				// Tyto texty signalizují, že jde o stacktrace který mě nezajímá (systémové vnitřnosti a vrhač chyb):
				string lineLower = line.ToLower();
				if (lineLower.Contains(" system.") ||
					lineLower.Contains(" noris.schedule.support.throw."))
					continue;

				errorPoint = line;
				break;
			}
			return errorPoint;
		}
		/// <summary>
		/// Pokud existuje soubor jména shodného, jaké má mít auditlog, pak takový soubor přejmenuje nebo smaže. 
		/// Prostě jej odstraní.
		/// </summary>
		private void _AuditClear()
		{
			string auditFile = AuditFile;
			string auditPath =  Path.GetDirectoryName(auditFile);
			string auditExte =  Path.GetExtension(auditFile);
			string auditNmwx =  Path.GetFileNameWithoutExtension(auditFile);

			// Pojmenovat current auditlog soubor tak, aby měl za jménem suffix (datum, čas, [číslo]):
			if (!File.Exists(auditFile)) return;
			FileInfo current = new FileInfo(auditFile);
			string auditBase = Path.Combine(auditPath, auditNmwx);           // c:\adresář\errlog
			auditBase = auditBase + "_" + current.LastWriteTime.ToString("yyyyMMdd_HHmmss");

			string auditRename = null;
			for (int t = 0; t < 100; t++)
			{
				auditRename = auditBase + (t == 0 ? "" : t.ToString("D2")) + auditExte;
				if (!File.Exists(auditRename))
					break;
				auditRename = null;
			}
			if (auditRename == null)
				File.Delete(auditFile);
			else
				File.Move(auditFile, auditRename);
			
			// Najít současné auditlogy a pokud je jich více, než MAX (30 kousků), pak ty přebytečné smazat:
			DirectoryInfo di = new DirectoryInfo(auditPath);
			FileInfo[] auditOldFiles = di.GetFiles(auditNmwx + "_*" + auditExte);
			int auditMax = 30;
			if (auditOldFiles.Length > auditMax)
			{
				List<FileInfo> oldFiles = new List<FileInfo>(auditOldFiles);
				oldFiles.Sort((a, b) => a.Name.CompareTo(b.Name));         // Setřídím podle jména (jméno nese datum, viz dříve proměnná auditBase)
				int last = oldFiles.Count - auditMax;                      // Smažu prvních tolik souborů, aby zbylo posledních (auditMax) položek:
				for (int ax = 0; ax < last; ax++)
					File.Delete(oldFiles[ax].FullName);
			}

            // Připravit úvodní řádek auditlogu, fyzicky se vypíše před první vypisovanou zprávu:
            this._AuditTitle = _AuditGetTitle();
		}
        private string _AuditTitle;
		/// <summary>
		/// Soubor (holý název), do něhož se budou auditovat problémy a výsledky
		/// </summary>
		internal static string AuditName { get { return "auditlog.csv"; } }
		/// <summary>
		/// Soubor (včetně adresáře), do něhož se budou auditovat problémy a výsledky.
        /// Soubor má strukturu řádků (UTF8), kde jednotlivé údaje jsou odděleny tabulátorem, a řádky jsou odděleny (Environment.NewLine).
        /// Sloupce mají význam, počínaje 1:
        /// 1: datum a čas ve formátu yyyy-MM-dd HH:mm:ss.f
        /// 2: typ řádku (jedno písmeno: N=None, I=Info, W=Warning, C=Completed, B=BreakError, D=DataError, E=ApplicationError, S=SystemError, Q=SqlError, M=Microsoft.NET error)
        /// 3: text zprávy / text chyby (Exception.Message)
        /// 4: řádek kódu, který způsobil chybu
        /// 5: číslo Green třídy pro kontext (ErrorWithContext)
        /// 6: číslo Green záznamu pro kontext (ErrorWithContext)
        /// 7: číslo Green pořadače pro kontext (ErrorWithContext)
        /// </summary>
		internal static string AuditFile { get { return _AuditGetFileName(); } }
		private static string _AuditGetFileName()
		{
			// Pozor: tento soubor následně může číst Noris, pokud tedy dojde ke změně v umístění, je nutno synchronně změnit metodu:
			// Noris.LCS.Manufacturing.SchedulerSupportCwl._ResultGetFileForApp() v App\Lcs\Manufacturing\SchedulerCwl.cs !
			return Path.Combine(TracePath, AuditName);
		}
		#endregion
        #region TRACE CSV
        /// <summary>
        /// Do trace zapíše řádek (typu System) s danými informacemi
        /// </summary>
        /// <param name="level"></param>
        /// <param name="type"></param>
        /// <param name="method"></param>
        /// <param name="keyword"></param>
        /// <param name="user"></param>
        public static void TraceSaveLine(string level, string type, string method, string keyword, params string[] user)
        {
            Current._TraceSaveInfo(level, "S", "", type, method, keyword, user);
        }
        /// <summary>
        /// Do trace zapíše řádek (typu System) s danými informacemi
        /// </summary>
        /// <param name="level"></param>
        /// <param name="type"></param>
        /// <param name="method"></param>
        /// <param name="keyword"></param>
        /// <param name="user"></param>
        public static void TraceSaveLine(int beDepth, string bse, string type, string method, string keyword, params string[] user)
        {
            Current._TraceSaveInfo("I", bse, beDepth.ToString(), type, method, keyword, user);
        }
        /// <summary>
        /// Do trace zapíše řádek typu Begin s danými informacemi, a vrátí objekt IDisposable (třídy TraceScope), který při svém Dispose zapíše řádek typu End.
        /// Typické použití je tedy: using (Steward.TraceScopeBegin(...)) { výkonný kód }.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="method"></param>
        /// <param name="keyword"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public static TraceScope TraceScopeBegin(string type, string method, string keyword, params string[] user)
        {
            // Pokud jsem já potlačen, nebudu používat další číslo BeDepth, ale vrátím Empty objekt. Je funkční z hlediska Suppress vnořených scope, ale nezapisuje svůj Begin a End do trace.
            if (Current._TraceIsSuppresed(type, method, keyword))
                return TraceScope.Empty;

            int beDepth = ++Current.__TraceScopeNumber;
            TraceScope scope = new TraceScope(beDepth, type, method, keyword, user);       // Konstruktor sám zapíše řádek Begin.    Následně Dispose objektu vypíše řádek End.
            return scope;
        }
        /// <summary>
        /// Obsahuje plný název adresáře App/Trace, v případě potřeby jej vytvoří.
        /// </summary>
        public static string TracePath
        {
            get
            {
                if (Current.__TracePath == null)
                {
                    string path = Path.Combine(Steward.ExecutablePath, "Trace");
                    if (!Directory.Exists(path))
                        Directory.CreateDirectory(path);
                    Current.__TracePath = path;
                }
                return Current.__TracePath;
            }
        }
        #region Potlačení určitých zpráv
        /// <summary>
        /// Přidá položku do seznamu položek, které jsou potlačeny při tvorbě trace
        /// </summary>
        /// <param name="suppress"></param>
        internal static void TraceSuppressAdd(TraceSuppressInfo suppress)
        {
            if (suppress != null)
                Current._TraceSuppressList.Add(suppress);
        }
        /// <summary>
        /// Odebere položku ze seznamu položek, které jsou potlačeny při tvorbě trace
        /// </summary>
        /// <param name="suppress"></param>
        internal static void TraceSuppressRemove(IEnumerable<TraceSuppressInfo> suppresses)
        {
            if (suppresses != null)
                Current._TraceSuppressRemove(suppresses);
        }
        /// <summary>
        /// Odebere položku ze seznamu položek, které jsou potlačeny při tvorbě trace
        /// </summary>
        /// <param name="suppress"></param>
        internal static void TraceSuppressRemove(TraceSuppressInfo suppress)
        {
            if (suppress != null)
                Current._TraceSuppressRemove(suppress);
        }
        private void _TraceSuppressRemove(IEnumerable<TraceSuppressInfo> suppresses)
        {
            if (suppresses == null) return;
            foreach (TraceSuppressInfo suppress in suppresses)
                _TraceSuppressRemove(suppress);
        }
        private void _TraceSuppressRemove(TraceSuppressInfo suppress)
        {
            if (suppress == null) return;
            List<TraceSuppressInfo> list = this._TraceSuppressList;
            list.RemoveAll(s => Object.ReferenceEquals(s, suppress));
        }
        private List<TraceSuppressInfo> _TraceSuppressList
        {
            get
            {
                if (this.__TraceSuppressList == null)
                    this.__TraceSuppressList = new List<TraceSuppressInfo>();
                return this.__TraceSuppressList;
            }
        }
        /// <summary>
        /// Vrátí true, pokud informace daného typu je potlačena (true = nevypisovat, false = vypisovat)
        /// </summary>
        /// <param name="type"></param>
        /// <param name="method"></param>
        /// <param name="keyword"></param>
        /// <returns></returns>
        private bool _TraceIsSuppresed(string type, string method, string keyword)
        {
            if (this.__TraceSuppressList == null) return false;
            return this.__TraceSuppressList.Any(s => s.IsCorresponding(type, method, keyword));
        }
        private List<TraceSuppressInfo> __TraceSuppressList;
        #endregion
        #region Privátní metody a proměnné, veskrze instanční
        /// <summary>
        /// Do trace zapíše řádek (typu System) s danými informacemi
        /// </summary>
        /// <param name="level"></param>
        /// <param name="bse"></param>
        /// <param name="beDepth"></param>
        /// <param name="type"></param>
        /// <param name="method"></param>
        /// <param name="keyword"></param>
        /// <param name="user"></param>
        private void _TraceSaveInfo(string level, string bse, string beDepth, string type, string method, string keyword, string[] user)
        {
            if (this._TraceIsSuppresed(type, method, keyword))
                return;

            string delim = ";";
            string login = _TraceToItem(Steward.CurrentUser.Login);
            string line = 
                DateTime.Now.ToString("F", this._TraceDtfi) + delim +  // Date;Time;
                delim +                          // Offset;
                "0" + delim +                    // GCMemory;
                delim +                          // Stack;
                delim +                          // Assembly;
                _TraceToItem(level) + delim +    // Level;
                _TraceToItem(bse) + delim +      // BSE;
                _TraceToItem(beDepth) + delim +  // BEDepth;
                "0" + delim +                    // BEOffset;
                "0" + delim +                    // RtOffset;
                login + delim +                  // Login;
                "0" + delim +                    // SecureId;
                "0" + delim +                    // ThreadId;
                "0" + delim +                    // RoundtripId;
                "0" + delim +                    // PageId;
                _TraceToItem(type) + delim +     // Type;
                _TraceToItem(method) + delim +   // Method;
                _TraceToItem(keyword);           // Keyword;
            if (user != null)
            {
                foreach (string usr in user)
                    line += delim + _TraceToItem(usr);
            }
            this._TraceCurrentStream.WriteLine(line);
        }
        /// <summary>
        /// Vrátí daný string upravený tak, aby mohl jít do Trace (tzn. neobsahuje Tab, Cr, Lf, středník)
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private string _TraceToItem(string text)
        {
            if (text == null) return "";
            if (text.Contains("\t")) text = text.Replace("\t", " ");
            if (text.Contains("\r")) text = text.Replace("\r", " ");
            if (text.Contains("\n")) text = text.Replace("\n", "");
            if (text.Contains(";")) text = text.Replace(";", ",");
            return text;
        }
        /// <summary>
        /// Zavře aktuální trace
        /// </summary>
        private void _TraceClose()
        {
            if (this.__TraceCurrentStream != null)
            {
                this.__TraceCurrentStream.Flush();
                this.__TraceCurrentStream.Close();
                this.__TraceCurrentStream = null;
                this.__TraceCurrentFile = null;
            }
        }
        /// <summary>
        /// Obsahuje plný název aktuálního trace souboru. V případě potřeby jej dohledá (ale soubor nezakládá, stream _TraceCurrentStream neotevírá).
        /// </summary>
        private string _TraceCurrentFile
        {
            get
            {
                if (!__TraceCurrentNumber.HasValue)
                {
                    this.__TraceCurrentFile = null;
                    if (this.__TraceCurrentStream != null)
                    {
                        this.__TraceCurrentStream.Close();
                        this.__TraceCurrentStream.Dispose();
                    }
                    this.__TraceCurrentStream = null;
                    string fp = Path.Combine(TracePath, "Trace_Scheduler_" + DateTime.Now.ToString("yyMMdd") + "_");
                    for (int fn = 0; fn < 1000; fn++)
                    {
                        string name = fp + fn.ToString("000") + ".csv";
                        if (fn == 999 || !File.Exists(name))
                        {
                            this.__TraceCurrentNumber = fn;
                            this.__TraceCurrentFile = name;
                            break;
                        }
                    }
                }
                return this.__TraceCurrentFile;
            }
        }
        /// <summary>
        /// Obsahuje platný otevřený stream, do něhož je možno zapisovat trace data.
        /// Stream je otevřený a obsahuje title line.
        /// </summary>
        private System.IO.StreamWriter _TraceCurrentStream
        {
            get
            {
                if (this.__TraceCurrentStream == null)
                {
                    string fileName = this._TraceCurrentFile;
                    this.__TraceCurrentStream = new StreamWriter(fileName, false, Encoding.UTF8);
                    this.__TraceCurrentStream.WriteLine(_TRACE_TITLE);
                }
                return this.__TraceCurrentStream;
            }
        }
        private System.Globalization.DateTimeFormatInfo _TraceDtfi
        {
            get
            {
                if (this.__TraceDtfi == null)
                {
                    this.__TraceDtfi = new System.Globalization.DateTimeFormatInfo();
                    this.__TraceDtfi.FullDateTimePattern = "yyyy/MM/dd;HH:mm:ss.fff";
                }
                return this.__TraceDtfi;
            }
        }
        private string __TracePath = null;
        private int? __TraceCurrentNumber = null;
        private string __TraceCurrentFile = null;
        private System.IO.StreamWriter __TraceCurrentStream = null;
        private int __TraceScopeNumber = 0;
        private System.Globalization.DateTimeFormatInfo __TraceDtfi = null;
        private const string _TRACE_TITLE = "Date;Time;Offset;GCMemory;Stack;Assembly;Level;BSE;BEDepth;BEOffset;RtOffset;Login;SecureId;ThreadId;RoundtripId;PageId;Type;Method;Keyword;User0;User1;User2";
        #endregion
        #endregion
        #region LOGIN
        /// <summary>
		/// Metoda zajistí okno Login, pokud je zapotřebí.
		/// </summary>
		private bool _LoginWhenRequired(int functionNumber, bool dataReady)
		{
			bool isLogged = false;
			NorisUserCls user = null;
			ConnectProfile dbProfile = null;

			// A) Login při spouštění z Norisovské funkce (parametry: Us, Sc/Sd, D):
			if (!isLogged)
				isLogged = this._LoginByGreenCwl(out user, out dbProfile);

			// B) Login as LCS:
			if (!isLogged)
				isLogged = this._LoginAsLcs(out user, out dbProfile);

			// C) Login podle parametrů N-P-D:
			if (!isLogged)
				isLogged = this._LoginByParams(out user, out dbProfile);

			// D) Login přes okno:
			if (!isLogged)
				isLogged = this._LoginViaForm(out user, out dbProfile);

			// Nepřihlášen:
			if (!isLogged)
				// Application.Exit();
				return false;

			// Ověřit práva na funkci:
			if (functionNumber > 0 && !this._LoginCheckPermissions(dbProfile, user, functionNumber))
				return false;

			// Přihlásit se:
			Steward.Login(dbProfile, user, dataReady);

			return true;
		}
		/// <summary>
		/// Pokud jsou v parametrech spouštění aplikace uvedeny parametry pro spuštění z Helios Green,
		/// zkusí se podle nich přihlásit k zadanému profilu a uživateli.
		/// </summary>
		/// <param name="user"></param>
		/// <param name="dbProfile"></param>
		/// <returns></returns>
		private bool _LoginByGreenCwl(out NorisUserCls user, out ConnectProfile dbProfile)
		{
			user = null;
			dbProfile = null;

			string userSubjText = this._ParamGet("Us", false);
			string connStringTxt = this._ParamGet("Sd", false);
			string connStringB64 = this._ParamGet("Sc", false);
			string profileName = this._ParamGet("D", false);

			// Pokud nejsou zadané parametry, tak to tudy nepůjde:
			if (userSubjText == null || (connStringTxt == null && connStringB64 == null) || profileName == null)
				return false;

			// Pokud není zadané číslo uživatele, skončíme:
			int userSubject;
			if (!Int32.TryParse(userSubjText, out userSubject))
				return false;
			if (userSubject <= 0)
				return false;

			// Connect string z Greenu:
			if (connStringTxt == null && connStringB64 != null)
				connStringTxt = Encoding.UTF8.GetString(Convert.FromBase64String(connStringB64));
			if (String.IsNullOrEmpty(connStringTxt))
				return false;

			// Zkusím najít zmíněný datový profil:
			ConnectProfile paramProfile = Steward.AppConfig.DbProfileListFind(profileName, false);
			if (paramProfile == null)
				paramProfile = Steward.AppConfig.DbProfileListAddNew(profileName);

            Steward.AuditInfo("Attempt to Login (Us + Sd/Sc + D)");

            // Connect string do profilu:
			paramProfile.ProfileConnString = connStringTxt;

			// Najít uživatele, v uvedené databázi:
			bool isLogged = false;
			string sql = NorisUserCls.SqlSelect + " WHERE lcs.noris_user.osoba = " + SqlFormat.ToSql(userSubject);
			try
			{
				using (var userConn = Noris.Schedule.Support.Sql.DataConnect.UserConnectBegin(paramProfile))
				{
					user = Db_Layer.GetRecord<NorisUserCls>(sql);
					if (user != null)
					{
						isLogged = true;
						dbProfile = paramProfile;
					}
				}
			}
			catch (SqdException exc)
			{
				// Výjimku utopím, prostě se jako LCS nepřihlásíme:
				Steward.Audit(AuditRowType.Info, "Nelze se přihlásit k danému profilu, pokus o připojení hlásí chybu: " + exc.Message, exc);
				user = null;
			}

			// Data do výstupních proměnných:
			dbProfile = paramProfile;

            if (isLogged)
                Steward.Audit(AuditRowType.Info, "Přihlášení dle parametrů: uživatel " + user.ReferNazev + ", profil: " + dbProfile.ProfileName);


			// Pokud jsme se přihlásili touto cestou, pak (jedině tehdy) můžeme akceptovat argument ProfileList=Disable:
			if (isLogged)
			{
				string pflDis = this._ParamGet("ProfileList", false);
				if (pflDis != null && pflDis == "Disable")
					Steward.AppConfig.DbProfileListDeleteAllExcept(paramProfile.ProfileName);
			}

			return isLogged;
		}
		/// <summary>
		/// Pokud je v parametrech spouštění aplikace uveden parametr "Login=AsLCS", 
		/// zkusí se přihlásit jako LCS k posledně platnému profilu.
		/// </summary>
		private bool _LoginAsLcs(out NorisUserCls user, out ConnectProfile dbProfile)
		{
			user = null;
			dbProfile = null;

			string paramLoginLcs = this._ParamGet("Login", false);
			if (String.IsNullOrEmpty(paramLoginLcs) || paramLoginLcs != "AsLCS")
				return false;

			bool isLogged = false;

            Steward.AuditInfo("Attempt to Login (as LCS)");

            string sql = NorisUserCls.SqlSelect + " WHERE lcs.noris_user.login = " + SqlFormat.ToSql("lcs");

			string paramLoginData = this._ParamGet("D", true);
			if (paramLoginData != null)
			{
				// S uvedeným profilem:
				ConnectProfile paramProfile = Steward.AppConfig.DbProfileListFind(paramLoginData, false);
				if (paramProfile == null)
					return false;

				try
				{
					using (var userConn = Noris.Schedule.Support.Sql.DataConnect.UserConnectBegin(paramProfile))
					{
						user = Db_Layer.GetRecord<NorisUserCls>(sql);
						if (user != null)
						{
							isLogged = true;
							dbProfile = paramProfile;
						}
					}
				}
				catch (SqdException exc)
				{
					// Výjimku utopím, prostě se jako LCS nepřihlásíme:
					user = null;
				}

			}
			else
			{	// Bez uvedení profilu:
				// Zkusím se připojit přes standardní connect (metodou Db_Layer.GetRecord(), bez otevření UserConnection):
				try
				{
					user = Db_Layer.GetRecord<NorisUserCls>(sql);
					if (user != null && user.RecordNumber > 0)
					{
						dbProfile = Steward.Connect;
						isLogged = true;
					}
				}
				catch (Exception exc)
				{
					// Výjimku utopím, prostě se jako LCS nepřihlásíme:
                    Steward.Audit(AuditRowType.Info, "Pokud o přihlášení AsLcs, chyba.", exc);
					user = null;
				}
			}
            if (isLogged)
                Steward.Audit(AuditRowType.Info, "Přihlášení dle požadavku: uživatel " + user.ReferNazev + ", profil: " + dbProfile.ProfileName);

			return isLogged;
		}
		/// <summary>
		/// Zkusí se přihlásit podle daných parametrů
		/// </summary>
		/// <param name="user"></param>
		/// <returns></returns>
		private bool _LoginByParams(out NorisUserCls user, out ConnectProfile dbProfile)
		{
			user = null;
			dbProfile = null;

			string login = this._ParamGet("N", true);
			string password = this._ParamGet("P", true);
			string profileName = this._ParamGet("D", true);
			if (login == null || profileName == null)
				return false;

			if (String.IsNullOrEmpty(profileName)) return false;
			ConnectProfile profile = Steward.AppConfig.DbProfileList.Find(p => p.ProfileName == profileName);
			if (profile == null) return false;

            Steward.AuditInfo("Attempt to Login (N + P + D)");

            string error;
			bool isLogged = _TryLogin(login, password, dbProfile, out error, out user);
			if (isLogged)
				dbProfile = profile;

            if (isLogged)
                Steward.Audit(AuditRowType.Info, "Přihlášení dle parametrů NPD: uživatel " + user.ReferNazev + ", profil: " + dbProfile.ProfileName);

			return isLogged;
		}
		/// <summary>
		/// Otevře okno pro zalogování
		/// </summary>
		/// <returns></returns>
		private bool _LoginViaForm(out NorisUserCls user, out ConnectProfile dbProfile)
		{
			bool isLogged = false;
			user = null;
			dbProfile = null;

			using (Noris.Schedule.Support.Forms.LoginForm loginForm = new Noris.Schedule.Support.Forms.LoginForm())
			{
				loginForm.ShowDialog();

				if (loginForm.DialogResult == DialogResult.OK)
				{
					user = loginForm.LoggedUser;
					dbProfile = loginForm.LoggedDbProfile;
					isLogged = true;
				}
			}

            if (isLogged)
                Steward.Audit(AuditRowType.Info, "Přihlášen uživatel " + user.ReferNazev + ", profil: " + dbProfile.ProfileName);

            return isLogged;
		}
		/// <summary>
		/// Metoda ověří, zda dané přihlašovací jsou korektní a funkční.
		/// Metoda vrací true = vše je OK / false = nelze se přilogovat.
		/// </summary>
		/// <param name="login">Jméno uživatele</param>
		/// <param name="password">Heslo</param>
		/// <param name="dbProfile">DB profil</param>
		/// <param name="error">Out text problému</param>
		/// <param name="user">Out uživatel, který danému loginu vyhovuje</param>
		/// <returns></returns>
		private bool _TryLogin(string login, string password, ConnectProfile dbProfile, out string error, out NorisUserCls user)
		{
			error = null;
			user = null;
			if (String.IsNullOrEmpty(login))
			{
				error = MessageInfo.Get("Login uživatele je třeba zadat.");
				return false;
			}
			if (dbProfile == null)
			{
				error = MessageInfo.Get("Vybraný profil databáze neexistuje.");
				return false;
			}
			string connectString = dbProfile.ProfileConnString;
			if (String.IsNullOrEmpty(connectString))
			{
				error = MessageInfo.Get("Vybraný profil databáze nemá správnou definici připojovacích parametrů.");
				return false;
			}

			try
			{
                using (var userConn = Noris.Schedule.Support.Sql.DataConnect.UserConnectBegin(dbProfile, IsolationLevel.ReadUncommitted))
				{
					string sql = NorisUserCls.SqlSelect + " WHERE lcs.noris_user.login = " + SqlFormat.ToSql(login);

					// Načtu data uživatele:
					user = Db_Layer.GetRecord<NorisUserCls>(sql);
					if (user == null)
						error = MessageInfo.Get("Zvolený uživatel a/nebo heslo ve zvolené databázi neexistuje.");

					// Pokud tento uživatel má v Greenu uvedeno heslo, pak jej ověříme:
					else if (!String.IsNullOrEmpty(user.Heslo))
					{
						if (!Steward.NorisPasswordCompareSignature(password, user.Heslo))
						{
							error = MessageInfo.Get("Zvolený uživatel a/nebo heslo ve zvolené databázi neexistuje.");
							user = null;
						}
					}
				}
			}
			catch (SqdException exc)
			{
				error = MessageInfo.Get("K vybranému profilu se nepodařilo připojit." + Environment.NewLine) + exc.AllMessages;
			}
            catch (Exception exc)
            {
                error = MessageInfo.Get("K vybranému profilu se nepodařilo připojit." + Environment.NewLine) + exc.Message;
            }
            return (user != null);
		}
		/// <summary>
		/// Metoda ověří práva daného uživatele v dané databázi, zda má přístup k dané funkci.
		/// Metoda zobrazí Dialog.ShowWarning(), když uživatel nemá práva. Vrací true / false.
		/// </summary>
		/// <param name="dbProfile">Databázový profil</param>
		/// <param name="user">Uživatel</param>
		/// <param name="functionNumber">Číslo funkce</param>
		/// <returns></returns>
		private bool _LoginCheckPermissions(ConnectProfile dbProfile, NorisUserCls user, int functionNumber)
		{
			string functionName;
			bool isPermitted = _GetFunctionPermission(dbProfile, user, functionNumber, out functionName);
			if (!isPermitted)
			{
				Dialog.ShowWarning(MessageInfo.Get("Přihlášený uživatel %0 nemá v databázi %1 právo ke spouštění funkce %2: %3.",
					user.Login + ": " + user.OsobaNazev,
					dbProfile.DataSource + "." + dbProfile.InitialCatalog,
					functionNumber, functionName));
			}
			return isPermitted;
		}
		/// <summary>
		/// Metoda zjistí, zda daný uživatel má v dané databázi, práva k dané funkci.
		/// Metoda nemá dialog, vrací true / false.
		/// </summary>
		/// <param name="dbProfile">Databázový profil</param>
		/// <param name="user">Uživatel</param>
		/// <param name="functionNumber">Číslo funkce</param>
		/// <param name="functionName">Out název dotazované funkce.</param>
		/// <returns></returns>
		private bool _GetFunctionPermission(ConnectProfile dbProfile, NorisUserCls user, int functionNumber, out string functionName)
		{
			functionName = "";
			bool permission = false;
			try
			{
				using (var userConn = Noris.Schedule.Support.Sql.DataConnect.UserConnectBegin(dbProfile))
				{
					permission = _GetFunctionPermission(user, functionNumber, out functionName);
				}
			}
			catch (SqdException exc)
			{
				// Výjimku utopím, prostě nemá právo:
				permission = false;
			}

			return permission;
		}
		/// <summary>
		/// Metoda zjistí, zda daný uživatel má v dané databázi, práva k dané funkci.
		/// Metoda nemá dialog, vrací true / false.
		/// </summary>
		/// <param name="dbProfile">Databázový profil</param>
		/// <param name="user">Uživatel</param>
		/// <param name="functionNumber">Číslo funkce</param>
		/// <param name="functionName">Out název dotazované funkce.</param>
		/// <returns></returns>
		private bool _GetFunctionPermission(NorisUserCls user, int functionNumber, out string functionName)
		{
			// Jméno funkce:
			functionName = Db_Layer.GetItem<string>("SELECT f.nazev FROM lcs.funjects f WHERE cislo_funjectu = " + SqlFormat.ToSql(functionNumber));

            // Testování práva je možno potlačit parametrem /AllRight:
            if (Steward.ParamGet("/AllRight", false) != null)
                return true;

			// Práva zjišťuji jen tehdy, když existuje tabulka:
			Green.DbTableInfo table;
			if (!Green.Repository.Tables.TryLoadFromDatabase("lcs.permissions", out table))
				// Pokud tabulka neexistuje, není to chyba na naší straně a přístup dovolíme:
				return true;

			// Detekce permission:
			string sql = @"SELECT top 1 cislo_nonsubjektu FROM lcs.permissions p
WHERE p.cislo_objektu = @funct /*cislo_funkce*/
  AND p.operace = (select cislo_nonsubjektu from lcs.noris_permission_list_of_fnc where upper(rtrim(syst_nazev_oper)) = 'EEXE')
  AND (p.zamid = @zamid or exists (select 1 from lcs.subjekt_hledspec h where h.cislo_subjektu=@zamid and p.groupid=h.cislo_selected))
  AND p.permission_flag = 0";
			sql = sql.Replace("@funct", SqlFormat.ToSql(functionNumber));      // lcs.permissions.cislo_objektu = číslo funkce
			sql = sql.Replace("@zamid", SqlFormat.ToSql(user.Osoba));          // lcs.noris_user.osoba = vztah na zaměstnance (lcs.zamestnanci.cislo_subjektu) = zamid.

			int cns = Db_Layer.GetItem<int>(sql);
			return (cns > 0);
		}
		/// <summary>
		/// Metoda zjistí, zda daný uživatel má v dané databázi, práva k dané funkci.
		/// Metoda nemá dialog, vrací true / false.
		/// </summary>
		/// <param name="user">Uživatel</param>
		/// <param name="functionNumber">Číslo funkce</param>
		/// <returns></returns>
		private bool _GetFunctionPermission(NorisUserCls user, int functionNumber)
		{
			string functionName;
			return _GetFunctionPermission(user, functionNumber, out functionName);
		}
		#endregion
		#endregion
	}
    #region IDisposable objekt, řešící Trace párové události (Begin, End)
    /// <summary>
    /// IDisposable objekt, řešící Trace párové události (Begin, End)
    /// </summary>
    public class TraceScope : IDisposable
    {
        internal TraceScope(int beDepth, string type, string method, string keyword, string[] user)
        {
            this._BEDepth = beDepth;
            this._Type = type;
            this._Method = method;
            this._Keyword = keyword;
            this._User = user;
            this._TraceSaveLine("B");
        }
        private TraceScope()
        {
            this.IsSuppressed = true;
        }
        /// <summary>
        /// Obsahuje (vrátí nový objekt) prázdný scope, který nepracuje.
        /// Použije se tehdy když by měl vzniknout scope, který je dle podmínek dřívějších scope potlačený.
        /// Tento scope se nevepisuje do trace, ale umožňuje dále potlačit jeho podřízené scope.
        /// </summary>
        internal static TraceScope Empty
        {
            get
            {
                return new TraceScope();
            }
        }
        /// <summary>
        /// Příznak, že tento scope (jeho Begin a End) se nevpisuje do trace, protože je potlačen některým vyšším scope.
        /// </summary>
        public bool IsSuppressed { get; private set; }
        void IDisposable.Dispose()
        {
            this._SuppressListClear();
            this._TraceSaveLine("E");
        }
        private void _TraceSaveLine(string bse)
        {
            if (!this.IsSuppressed)
                Steward.TraceSaveLine(this._BEDepth, bse, this._Type, this._Method, this._Keyword, this._User);
        }
        /// <summary>
        /// Hodnoty do pole User, jsou zadané na počátku, a pokud se v průběhu života změní budou uvedeny v záznamu End ty změněné.
        /// </summary>
        public string[] User { get { return this._User; } set { this._User = value; } }
        private int _BEDepth;
        private string _Type;
        private string _Method;
        private string _Keyword;
        private string[] _User;
        /// <summary>
        /// Potlačí následující zápisy do trace, daného typu.
        /// Zápisy budou obnoveny po ukončení tohoto scope.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="method"></param>
        /// <param name="keyword"></param>
        public void SuppressInnerTrace(string type, string method, string keyword)
        {
            if (this._SuppressList == null)
                this._SuppressList = new List<TraceSuppressInfo>();
            
            TraceSuppressInfo suppress = new TraceSuppressInfo(type, method, keyword);
            this._SuppressList.Add(suppress);

            Steward.TraceSuppressAdd(suppress);
        }
        private void _SuppressListClear()
        {
            if (this._SuppressList != null)
                Steward.TraceSuppressRemove(this._SuppressList);
        }
        private List<TraceSuppressInfo> _SuppressList;
    }
    internal class TraceSuppressInfo
    {
        internal TraceSuppressInfo(string type, string method, string keyword)
        {
            this.Type = type;
            this.Method = method;
            this.Keyword = keyword;
        }
        public string Type { get; private set; }
        public string Method { get; private set; }
        public string Keyword { get; private set; }
        /// <summary>
        /// Vrátí true, pokud data v tomto objektu odpovídají datům vstupujícím.
        /// Přičemž pokud je v tomto objektu v některé property NULL, pak to vyhovuje bez ohledu na aktuální hodnotu odpovídající property.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="method"></param>
        /// <param name="keyword"></param>
        /// <returns></returns>
        internal bool IsCorresponding(string type, string method, string keyword)
        {
            return (
                (this.Type == null || String.Equals(this.Type, type, StringComparison.InvariantCultureIgnoreCase)) &&
                (this.Method == null || String.Equals(this.Method, method, StringComparison.InvariantCultureIgnoreCase)) &&
                (this.Keyword == null || String.Equals(this.Keyword, keyword, StringComparison.InvariantCultureIgnoreCase))
                    );
        }
    }
    #endregion
    #region CLASS NorisConfigCls : registr konfiguračních dat Norisu
    public class NorisConfigCls
	{
		#region KONSTRUKCE
		private NorisConfigCls()
		{ }
		/// <summary>
		/// Vrací vždy novou instanci konfiguračních dat, čerstvě načtených z Norisu.
		/// </summary>
		public static NorisConfigCls Current
		{
			get
			{
				NorisConfigCls config = new NorisConfigCls();
				// Tipy pro rozšiřování: 
				//  - přidat název db sloupce do selectu (a případně i další tabulku do selectu)
				//  - přidat instanční proměnnou shodného názvu, předřadit podtržítko, malými písmeny!
				//  - přidat property get
				string sql = "SELECT top 1 c.smtp_server, wf.email_account, wf.email_account_display_name, wf.reply_to_account, wf.reply_to_account_display_name FROM lcs.system_config c JOIN lcs.wf_modul_config wf on (1=1)";
				Db_Layer.LoadIntoRecord(config, sql);
				return config;
			}
		}
		private string _smtp_server;
		private string _email_account;
		private string _email_account_display_name;
		private string _reply_to_account;
		private string _reply_to_account_display_name;
		#endregion
		#region ZÁKLADNÍ PROPERTY
		/// <summary>
		/// Adresa SMTP serveru
		/// </summary>
		public string SmtpServer { get { return _smtp_server; } }
		/// <summary>
		/// Workflow: adresa odesilatele, email
		/// </summary>
		public string WfEmailAccount { get { return _email_account; } }
		/// <summary>
		/// Workflow: adresa odesilatele, text
		/// </summary>
		public string WfEmailAccountName { get { return _email_account_display_name; } }
		/// <summary>
		/// Workflow: adresa příjemce odpovědi, email
		/// </summary>
		public string WfEmailReplyTo { get { return _reply_to_account; } }
		/// <summary>
		/// Workflow: adresa příjemce odpovědi, text
		/// </summary>
		public string WfEmailReplyToName { get { return _reply_to_account_display_name; } }
		#endregion
		#region PODPORA ODESÍLÁNÍ E-MAILŮ
		/// <summary>
		/// MailAdress pro WfEmailAccount
		/// </summary>
		public System.Net.Mail.MailAddress WfEmailAccountAdress
		{
			get { return MailAdapter.GetMailAdress(WfEmailAccount, WfEmailAccountName); }
		}
		/// <summary>
		/// MailAdress pro WfEmailReplyTo
		/// </summary>
		public System.Net.Mail.MailAddress WfEmailReplyToAdress
		{
			get { return MailAdapter.GetMailAdress(WfEmailReplyTo, WfEmailReplyToName); }
		}
		#endregion
	}
	#endregion
	#region ENUM AuditRowType
	/// <summary>
	/// Druhy informací v auditlogu
	/// </summary>
	public enum AuditRowType
	{
		/// <summary>
		/// N: neurčený typ
		/// </summary>
		None = 1,
		/// <summary>
		/// I: Informace
		/// </summary>
		Info,
		/// <summary>
		/// W: Warning
		/// </summary>
		Warning,
		/// <summary>
		/// C: úspěšně dokončená operace
		/// </summary>
		Completed,
		/// <summary>
		/// B: break, přerušeno uživatelem
		/// </summary>
		BreakError,
		/// <summary>
		/// D: datová chyba na vstupním formuláři/vstupních parametrech
		/// </summary>
		InputError,
		/// <summary>
		/// E: Error, aplikační
		/// </summary>
		ApplicationError,
		/// <summary>
		/// S: Systémová chyba vrstvy, typicky vrstva Support nebo chyba sekvence operací, řízení programu (uživatel nemůže ovlivnit)
		/// </summary>
		CoreError,
		/// <summary>
		/// Q: SQL chyba, hlášená ze SQL operací
		/// </summary>
		SqlError,
		/// <summary>
		/// M: chyba neodchycená jinak než jako Exception, chyby .NET
		/// </summary>
		MsNetError
	}
	#endregion
}
